phantomjs-binaries 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/modules/utils.js ADDED
@@ -0,0 +1,581 @@
1
+ /*!
2
+ * Casper is a navigation utility for PhantomJS.
3
+ *
4
+ * Documentation: http://casperjs.org/
5
+ * Repository: http://github.com/n1k0/casperjs
6
+ *
7
+ * Copyright (c) 2011-2012 Nicolas Perriault
8
+ *
9
+ * Part of source code is Copyright Joyent, Inc. and other Node contributors.
10
+ *
11
+ * Permission is hereby granted, free of charge, to any person obtaining a
12
+ * copy of this software and associated documentation files (the "Software"),
13
+ * to deal in the Software without restriction, including without limitation
14
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
+ * and/or sell copies of the Software, and to permit persons to whom the
16
+ * Software is furnished to do so, subject to the following conditions:
17
+ *
18
+ * The above copyright notice and this permission notice shall be included
19
+ * in all copies or substantial portions of the Software.
20
+ *
21
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
+ * DEALINGS IN THE SOFTWARE.
28
+ *
29
+ */
30
+
31
+ /*global CasperError console exports phantom require*/
32
+
33
+ /**
34
+ * Provides a better typeof operator equivalent, able to retrieve the array
35
+ * type.
36
+ *
37
+ * CAVEAT: this function does not necessarilly map to classical js "type" names,
38
+ * notably a `null` will map to "null" instead of "object".
39
+ *
40
+ * @param mixed input
41
+ * @return String
42
+ * @see http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/
43
+ */
44
+ function betterTypeOf(input) {
45
+ "use strict";
46
+ switch (input) {
47
+ case undefined:
48
+ return 'undefined';
49
+ case null:
50
+ return 'null';
51
+ default:
52
+ try {
53
+ return Object.prototype.toString.call(input).match(/^\[object\s(.*)\]$/)[1].toLowerCase();
54
+ } catch (e) {
55
+ return typeof input;
56
+ }
57
+ }
58
+ }
59
+ exports.betterTypeOf = betterTypeOf;
60
+
61
+ /**
62
+ * Cleans a passed URL if it lacks a slash at the end when a sole domain is used.
63
+ *
64
+ * @param String url An HTTP URL
65
+ * @return String
66
+ */
67
+ function cleanUrl(url) {
68
+ "use strict";
69
+ var parts = /(https?):\/\/(.*)/i.exec(url);
70
+ if (!parts) {
71
+ return url;
72
+ }
73
+ var protocol = parts[1];
74
+ var subparts = parts[2].split('/');
75
+ if (subparts.length === 1) {
76
+ return format("%s://%s/", protocol, subparts[0]);
77
+ }
78
+ return url;
79
+ }
80
+ exports.cleanUrl = cleanUrl;
81
+
82
+ /**
83
+ * Clones an object.
84
+ *
85
+ * @param Mixed o
86
+ * @return Mixed
87
+ */
88
+ function clone(o) {
89
+ "use strict";
90
+ return JSON.parse(JSON.stringify(o));
91
+ }
92
+ exports.clone = clone;
93
+
94
+ /**
95
+ * Dumps a JSON representation of passed value to the console. Used for
96
+ * debugging purpose only.
97
+ *
98
+ * @param Mixed value
99
+ */
100
+ function dump(value) {
101
+ "use strict";
102
+ console.log(serialize(value, 4));
103
+ }
104
+ exports.dump = dump;
105
+
106
+ /**
107
+ * Tests equality between the two passed arguments.
108
+ *
109
+ * @param Mixed v1
110
+ * @param Mixed v2
111
+ * @param Boolean
112
+ */
113
+ function equals(v1, v2) {
114
+ "use strict";
115
+ if (isFunction(v1)) {
116
+ return v1.toString() === v2.toString();
117
+ }
118
+ if (v1 instanceof Object) {
119
+ if (Object.keys(v1).length !== Object.keys(v2).length) {
120
+ return false;
121
+ }
122
+ for (var k in v1) {
123
+ if (!equals(v1[k], v2[k])) {
124
+ return false;
125
+ }
126
+ }
127
+ return true;
128
+ }
129
+ return v1 === v2;
130
+ }
131
+ exports.equals = equals;
132
+
133
+ /**
134
+ * Returns the file extension in lower case.
135
+ *
136
+ * @param String file File path
137
+ * @return string
138
+ */
139
+ function fileExt(file) {
140
+ "use strict";
141
+ try {
142
+ return file.split('.').pop().toLowerCase().trim();
143
+ } catch(e) {
144
+ return '';
145
+ }
146
+ }
147
+ exports.fileExt = fileExt;
148
+
149
+ /**
150
+ * Takes a string and append blanks until the pad value is reached.
151
+ *
152
+ * @param String text
153
+ * @param Number pad Pad value (optional; default: 80)
154
+ * @return String
155
+ */
156
+ function fillBlanks(text, pad) {
157
+ "use strict";
158
+ pad = pad || 80;
159
+ if (text.length < pad) {
160
+ text += new Array(pad - text.length + 1).join(' ');
161
+ }
162
+ return text;
163
+ }
164
+ exports.fillBlanks = fillBlanks;
165
+
166
+ /**
167
+ * Formats a string with passed parameters. Ported from nodejs `util.format()`.
168
+ *
169
+ * @return String
170
+ */
171
+ function format(f) {
172
+ "use strict";
173
+ var i = 1;
174
+ var args = arguments;
175
+ var len = args.length;
176
+ var str = String(f).replace(/%[sdj%]/g, function _replace(x) {
177
+ if (i >= len) return x;
178
+ switch (x) {
179
+ case '%s':
180
+ return String(args[i++]);
181
+ case '%d':
182
+ return Number(args[i++]);
183
+ case '%j':
184
+ return JSON.stringify(args[i++]);
185
+ case '%%':
186
+ return '%';
187
+ default:
188
+ return x;
189
+ }
190
+ });
191
+ for (var x = args[i]; i < len; x = args[++i]) {
192
+ if (x === null || typeof x !== 'object') {
193
+ str += ' ' + x;
194
+ } else {
195
+ str += '[obj]';
196
+ }
197
+ }
198
+ return str;
199
+ }
200
+ exports.format = format;
201
+
202
+ /**
203
+ * Retrieves the value of an Object foreign property using a dot-separated
204
+ * path string.
205
+ *
206
+ * Beware, this function doesn't handle object key names containing a dot.
207
+ *
208
+ * @param Object obj The source object
209
+ * @param String path Dot separated path, eg. "x.y.z"
210
+ */
211
+ function getPropertyPath(obj, path) {
212
+ "use strict";
213
+ if (!isObject(obj) || !isString(path)) {
214
+ return undefined;
215
+ }
216
+ var value = obj;
217
+ path.split('.').forEach(function(property) {
218
+ if (typeof value === "object" && property in value) {
219
+ value = value[property];
220
+ } else {
221
+ value = undefined;
222
+ }
223
+ });
224
+ return value;
225
+ }
226
+ exports.getPropertyPath = getPropertyPath;
227
+
228
+ /**
229
+ * Inherit the prototype methods from one constructor into another.
230
+ *
231
+ * @param {function} ctor Constructor function which needs to inherit the
232
+ * prototype.
233
+ * @param {function} superCtor Constructor function to inherit prototype from.
234
+ */
235
+ function inherits(ctor, superCtor) {
236
+ "use strict";
237
+ ctor.super_ = ctor.__super__ = superCtor;
238
+ ctor.prototype = Object.create(superCtor.prototype, {
239
+ constructor: {
240
+ value: ctor,
241
+ enumerable: false,
242
+ writable: true,
243
+ configurable: true
244
+ }
245
+ });
246
+ }
247
+ exports.inherits = inherits;
248
+
249
+ /**
250
+ * Checks if value is a javascript Array
251
+ *
252
+ * @param mixed value
253
+ * @return Boolean
254
+ */
255
+ function isArray(value) {
256
+ "use strict";
257
+ return Array.isArray(value) || isType(value, "array");
258
+ }
259
+ exports.isArray = isArray;
260
+
261
+ /**
262
+ * Checks if passed argument is an instance of Capser object.
263
+ *
264
+ * @param mixed value
265
+ * @return Boolean
266
+ */
267
+ function isCasperObject(value) {
268
+ "use strict";
269
+ return value instanceof require('casper').Casper;
270
+ }
271
+ exports.isCasperObject = isCasperObject;
272
+
273
+ /**
274
+ * Checks if value is a phantomjs clipRect-compatible object
275
+ *
276
+ * @param mixed value
277
+ * @return Boolean
278
+ */
279
+ function isClipRect(value) {
280
+ "use strict";
281
+ return isType(value, "cliprect") || (
282
+ isObject(value) &&
283
+ isNumber(value.top) && isNumber(value.left) &&
284
+ isNumber(value.width) && isNumber(value.height)
285
+ );
286
+ }
287
+ exports.isClipRect = isClipRect;
288
+
289
+ /**
290
+ * Checks that the subject is falsy.
291
+ *
292
+ * @param Mixed subject Test subject
293
+ * @return Boolean
294
+ */
295
+ function isFalsy(subject) {
296
+ "use strict";
297
+ /*jshint eqeqeq:false*/
298
+ return !subject;
299
+ }
300
+ exports.isFalsy = isFalsy;
301
+ /**
302
+ * Checks if value is a javascript Function
303
+ *
304
+ * @param mixed value
305
+ * @return Boolean
306
+ */
307
+ function isFunction(value) {
308
+ "use strict";
309
+ return isType(value, "function");
310
+ }
311
+ exports.isFunction = isFunction;
312
+
313
+ /**
314
+ * Checks if passed resource involves an HTTP url.
315
+ *
316
+ * @param Object resource The PhantomJS HTTP resource object
317
+ * @return Boolean
318
+ */
319
+ function isHTTPResource(resource) {
320
+ "use strict";
321
+ return isObject(resource) && /^http/i.test(resource.url);
322
+ }
323
+ exports.isHTTPResource = isHTTPResource;
324
+
325
+ /**
326
+ * Checks if a file is apparently javascript compatible (.js or .coffee).
327
+ *
328
+ * @param String file Path to the file to test
329
+ * @return Boolean
330
+ */
331
+ function isJsFile(file) {
332
+ "use strict";
333
+ var ext = fileExt(file);
334
+ return isString(ext, "string") && ['js', 'coffee'].indexOf(ext) !== -1;
335
+ }
336
+ exports.isJsFile = isJsFile;
337
+
338
+ /**
339
+ * Checks if the provided value is null
340
+ *
341
+ * @return Boolean
342
+ */
343
+ function isNull(value) {
344
+ "use strict";
345
+ return isType(value, "null");
346
+ }
347
+ exports.isNull = isNull;
348
+
349
+ /**
350
+ * Checks if value is a javascript Number
351
+ *
352
+ * @param mixed value
353
+ * @return Boolean
354
+ */
355
+ function isNumber(value) {
356
+ "use strict";
357
+ return isType(value, "number");
358
+ }
359
+ exports.isNumber = isNumber;
360
+
361
+ /**
362
+ * Checks if value is a javascript Object
363
+ *
364
+ * @param mixed value
365
+ * @return Boolean
366
+ */
367
+ function isObject(value) {
368
+ "use strict";
369
+ var objectTypes = ["array", "object", "qtruntimeobject"];
370
+ return objectTypes.indexOf(betterTypeOf(value)) >= 0;
371
+ }
372
+ exports.isObject = isObject;
373
+
374
+ /**
375
+ * Checks if value is a javascript String
376
+ *
377
+ * @param mixed value
378
+ * @return Boolean
379
+ */
380
+ function isString(value) {
381
+ "use strict";
382
+ return isType(value, "string");
383
+ }
384
+ exports.isString = isString;
385
+
386
+ /**
387
+ * Checks that the subject is truthy.
388
+ *
389
+ * @param Mixed subject Test subject
390
+ * @return Boolean
391
+ */
392
+ function isTruthy(subject) {
393
+ "use strict";
394
+ /*jshint eqeqeq:false*/
395
+ return !!subject;
396
+ }
397
+ exports.isTruthy = isTruthy;
398
+
399
+ /**
400
+ * Shorthands for checking if a value is of the given type. Can check for
401
+ * arrays.
402
+ *
403
+ * @param mixed what The value to check
404
+ * @param String typeName The type name ("string", "number", "function", etc.)
405
+ * @return Boolean
406
+ */
407
+ function isType(what, typeName) {
408
+ "use strict";
409
+ if (typeof typeName !== "string" || !typeName) {
410
+ throw new CasperError("You must pass isType() a typeName string");
411
+ }
412
+ return betterTypeOf(what).toLowerCase() === typeName.toLowerCase();
413
+ }
414
+ exports.isType = isType;
415
+
416
+ /**
417
+ * Checks if the provided value is undefined
418
+ *
419
+ * @return Boolean
420
+ */
421
+ function isUndefined(value) {
422
+ "use strict";
423
+ return isType(value, "undefined");
424
+ }
425
+ exports.isUndefined = isUndefined;
426
+
427
+ /**
428
+ * Checks if value is a valid selector Object.
429
+ *
430
+ * @param mixed value
431
+ * @return Boolean
432
+ */
433
+ function isValidSelector(value) {
434
+ "use strict";
435
+ if (isString(value)) {
436
+ try {
437
+ // phantomjs env has a working document object, let's use it
438
+ document.querySelector(value);
439
+ } catch(e) {
440
+ if ('name' in e && e.name === 'SYNTAX_ERR') {
441
+ return false;
442
+ }
443
+ }
444
+ return true;
445
+ } else if (isObject(value)) {
446
+ if (!value.hasOwnProperty('type')) {
447
+ return false;
448
+ }
449
+ if (!value.hasOwnProperty('path')) {
450
+ return false;
451
+ }
452
+ if (['css', 'xpath'].indexOf(value.type) === -1) {
453
+ return false;
454
+ }
455
+ return true;
456
+ }
457
+ return false;
458
+ }
459
+ exports.isValidSelector = isValidSelector;
460
+
461
+ /**
462
+ * Checks if the provided var is a WebPage instance
463
+ *
464
+ * @param mixed what
465
+ * @return Boolean
466
+ */
467
+ function isWebPage(what) {
468
+ "use strict";
469
+ return betterTypeOf(what) === "qtruntimeobject" && what.objectName === 'WebPage';
470
+ }
471
+ exports.isWebPage = isWebPage;
472
+
473
+ /**
474
+ * Object recursive merging utility.
475
+ *
476
+ * @param Object origin the origin object
477
+ * @param Object add the object to merge data into origin
478
+ * @return Object
479
+ */
480
+ function mergeObjects(origin, add) {
481
+ "use strict";
482
+ for (var p in add) {
483
+ try {
484
+ if (add[p].constructor === Object) {
485
+ origin[p] = mergeObjects(origin[p], add[p]);
486
+ } else {
487
+ origin[p] = add[p];
488
+ }
489
+ } catch(e) {
490
+ origin[p] = add[p];
491
+ }
492
+ }
493
+ return origin;
494
+ }
495
+ exports.mergeObjects = mergeObjects;
496
+
497
+ /**
498
+ * Converts milliseconds to seconds and rounds the results to 3 digits accuracy.
499
+ *
500
+ * @param Number milliseconds
501
+ * @return Number seconds
502
+ */
503
+ function ms2seconds(milliseconds) {
504
+ "use strict";
505
+ return Math.round(milliseconds / 1000 * 1000) / 1000;
506
+ }
507
+ exports.ms2seconds = ms2seconds;
508
+
509
+ /**
510
+ * Creates an (SG|X)ML node element.
511
+ *
512
+ * @param String name The node name
513
+ * @param Object attributes Optional attributes
514
+ * @return HTMLElement
515
+ */
516
+ function node(name, attributes) {
517
+ "use strict";
518
+ var _node = document.createElement(name);
519
+ for (var attrName in attributes) {
520
+ var value = attributes[attrName];
521
+ if (attributes.hasOwnProperty(attrName) && isString(attrName)) {
522
+ _node.setAttribute(attrName, value);
523
+ }
524
+ }
525
+ return _node;
526
+ }
527
+ exports.node = node;
528
+
529
+ /**
530
+ * Maps an object to an array made from its values.
531
+ *
532
+ * @param Object obj
533
+ * @return Array
534
+ */
535
+ function objectValues(obj) {
536
+ "use strict";
537
+ return Object.keys(obj).map(function(arg) {
538
+ return obj[arg];
539
+ });
540
+ }
541
+ exports.objectValues = objectValues;
542
+
543
+ /**
544
+ * Serializes a value using JSON.
545
+ *
546
+ * @param Mixed value
547
+ * @return String
548
+ */
549
+ function serialize(value, indent) {
550
+ "use strict";
551
+ if (isArray(value)) {
552
+ value = value.map(function _map(prop) {
553
+ return isFunction(prop) ? prop.toString().replace(/\s{2,}/, '') : prop;
554
+ });
555
+ }
556
+ return JSON.stringify(value, null, indent);
557
+ }
558
+ exports.serialize = serialize;
559
+
560
+ /**
561
+ * Returns unique values from an array.
562
+ *
563
+ * Note: ugly code is ugly, but efficient: http://jsperf.com/array-unique2/8
564
+ *
565
+ * @param Array array
566
+ * @return Array
567
+ */
568
+ function unique(array) {
569
+ "use strict";
570
+ var o = {},
571
+ r = [];
572
+ for (var i = 0, len = array.length; i !== len; i++) {
573
+ var d = array[i];
574
+ if (o[d] !== 1) {
575
+ o[d] = 1;
576
+ r[r.length] = d;
577
+ }
578
+ }
579
+ return r;
580
+ }
581
+ exports.unique = unique;