sugar-rails 1.2.5.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +110 -28
- data/lib/generators/sugar/build/build_generator.rb +107 -0
- data/lib/generators/sugar/install/install_generator.rb +2 -2
- data/lib/sugar/rails/version.rb +2 -2
- data/sugar-rails.gemspec +1 -1
- data/vendor/assets/javascripts/precompiled/development/array.js +1212 -0
- data/vendor/assets/javascripts/precompiled/development/core.js +329 -0
- data/vendor/assets/javascripts/precompiled/development/date.js +2179 -0
- data/vendor/assets/javascripts/precompiled/development/date_locales.js +952 -0
- data/vendor/assets/javascripts/precompiled/development/date_ranges.js +183 -0
- data/vendor/assets/javascripts/precompiled/development/es5.js +443 -0
- data/vendor/assets/javascripts/precompiled/development/function.js +222 -0
- data/vendor/assets/javascripts/{sugar-inflections.js → precompiled/development/inflections.js} +51 -162
- data/vendor/assets/javascripts/precompiled/development/language.js +383 -0
- data/vendor/assets/javascripts/precompiled/development/number.js +422 -0
- data/vendor/assets/javascripts/precompiled/development/object.js +348 -0
- data/vendor/assets/javascripts/precompiled/development/regexp.js +92 -0
- data/vendor/assets/javascripts/precompiled/development/string.js +871 -0
- data/vendor/assets/javascripts/precompiled/minified/array.js +16 -0
- data/vendor/assets/javascripts/precompiled/minified/core.js +8 -0
- data/vendor/assets/javascripts/precompiled/minified/date.js +40 -0
- data/vendor/assets/javascripts/precompiled/minified/date_locales.js +38 -0
- data/vendor/assets/javascripts/precompiled/minified/date_ranges.js +3 -0
- data/vendor/assets/javascripts/precompiled/minified/es5.js +7 -0
- data/vendor/assets/javascripts/precompiled/minified/function.js +4 -0
- data/vendor/assets/javascripts/precompiled/minified/inflections.js +11 -0
- data/vendor/assets/javascripts/precompiled/minified/language.js +19 -0
- data/vendor/assets/javascripts/precompiled/minified/number.js +5 -0
- data/vendor/assets/javascripts/precompiled/minified/object.js +6 -0
- data/vendor/assets/javascripts/precompiled/minified/regexp.js +2 -0
- data/vendor/assets/javascripts/precompiled/minified/string.js +12 -0
- data/vendor/assets/javascripts/precompiled/readme.txt +3 -0
- data/vendor/assets/javascripts/sugar-development.js +8054 -0
- data/vendor/assets/javascripts/sugar-full.js +179 -0
- data/vendor/assets/javascripts/sugar.js +111 -6211
- metadata +35 -9
- data/vendor/assets/javascripts/sugar-core.js +0 -4001
- data/vendor/assets/javascripts/sugar-dates-only.js +0 -3121
- data/vendor/assets/javascripts/sugar-dates.js +0 -2210
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sugar-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: railties
|
@@ -72,18 +72,44 @@ files:
|
|
72
72
|
- LICENSE.txt
|
73
73
|
- README.md
|
74
74
|
- Rakefile
|
75
|
+
- lib/generators/sugar/build/build_generator.rb
|
75
76
|
- lib/generators/sugar/install/install_generator.rb
|
76
77
|
- lib/sugar-rails.rb
|
77
78
|
- lib/sugar/rails.rb
|
78
79
|
- lib/sugar/rails/engine.rb
|
79
80
|
- lib/sugar/rails/version.rb
|
80
81
|
- sugar-rails.gemspec
|
81
|
-
- vendor/assets/javascripts/
|
82
|
-
- vendor/assets/javascripts/
|
83
|
-
- vendor/assets/javascripts/
|
84
|
-
- vendor/assets/javascripts/
|
82
|
+
- vendor/assets/javascripts/precompiled/development/array.js
|
83
|
+
- vendor/assets/javascripts/precompiled/development/core.js
|
84
|
+
- vendor/assets/javascripts/precompiled/development/date.js
|
85
|
+
- vendor/assets/javascripts/precompiled/development/date_locales.js
|
86
|
+
- vendor/assets/javascripts/precompiled/development/date_ranges.js
|
87
|
+
- vendor/assets/javascripts/precompiled/development/es5.js
|
88
|
+
- vendor/assets/javascripts/precompiled/development/function.js
|
89
|
+
- vendor/assets/javascripts/precompiled/development/inflections.js
|
90
|
+
- vendor/assets/javascripts/precompiled/development/language.js
|
91
|
+
- vendor/assets/javascripts/precompiled/development/number.js
|
92
|
+
- vendor/assets/javascripts/precompiled/development/object.js
|
93
|
+
- vendor/assets/javascripts/precompiled/development/regexp.js
|
94
|
+
- vendor/assets/javascripts/precompiled/development/string.js
|
95
|
+
- vendor/assets/javascripts/precompiled/minified/array.js
|
96
|
+
- vendor/assets/javascripts/precompiled/minified/core.js
|
97
|
+
- vendor/assets/javascripts/precompiled/minified/date.js
|
98
|
+
- vendor/assets/javascripts/precompiled/minified/date_locales.js
|
99
|
+
- vendor/assets/javascripts/precompiled/minified/date_ranges.js
|
100
|
+
- vendor/assets/javascripts/precompiled/minified/es5.js
|
101
|
+
- vendor/assets/javascripts/precompiled/minified/function.js
|
102
|
+
- vendor/assets/javascripts/precompiled/minified/inflections.js
|
103
|
+
- vendor/assets/javascripts/precompiled/minified/language.js
|
104
|
+
- vendor/assets/javascripts/precompiled/minified/number.js
|
105
|
+
- vendor/assets/javascripts/precompiled/minified/object.js
|
106
|
+
- vendor/assets/javascripts/precompiled/minified/regexp.js
|
107
|
+
- vendor/assets/javascripts/precompiled/minified/string.js
|
108
|
+
- vendor/assets/javascripts/precompiled/readme.txt
|
109
|
+
- vendor/assets/javascripts/sugar-development.js
|
110
|
+
- vendor/assets/javascripts/sugar-full.js
|
85
111
|
- vendor/assets/javascripts/sugar.js
|
86
|
-
homepage:
|
112
|
+
homepage: http://phlippers.net/sugar-rails
|
87
113
|
licenses: []
|
88
114
|
post_install_message:
|
89
115
|
rdoc_options: []
|
@@ -97,7 +123,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
123
|
version: '0'
|
98
124
|
segments:
|
99
125
|
- 0
|
100
|
-
hash:
|
126
|
+
hash: -1062117382192115571
|
101
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
128
|
none: false
|
103
129
|
requirements:
|
@@ -106,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
106
132
|
version: '0'
|
107
133
|
segments:
|
108
134
|
- 0
|
109
|
-
hash:
|
135
|
+
hash: -1062117382192115571
|
110
136
|
requirements: []
|
111
137
|
rubyforge_project:
|
112
138
|
rubygems_version: 1.8.24
|
@@ -1,4001 +0,0 @@
|
|
1
|
-
// Google Closure Compiler will output a wrapping function here.
|
2
|
-
(function() {
|
3
|
-
|
4
|
-
// A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
|
5
|
-
var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, Undefined;
|
6
|
-
|
7
|
-
// defineProperty exists in IE8 but will error when trying to define a property on
|
8
|
-
// native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
|
9
|
-
var definePropertySupport = object.defineProperty && object.defineProperties;
|
10
|
-
|
11
|
-
// Class extending methods
|
12
|
-
|
13
|
-
function extend(klass, instance, override, methods) {
|
14
|
-
var extendee = instance ? klass.prototype : klass, original;
|
15
|
-
initializeClass(klass, instance, methods);
|
16
|
-
iterateOverObject(methods, function(name, method) {
|
17
|
-
original = extendee[name];
|
18
|
-
if(typeof override === 'function') {
|
19
|
-
method = wrapNative(extendee[name], method, override);
|
20
|
-
}
|
21
|
-
if(override !== false || !extendee[name]) {
|
22
|
-
defineProperty(extendee, name, method);
|
23
|
-
}
|
24
|
-
// If the method is internal to Sugar, then store a reference so it can be restored later.
|
25
|
-
klass['SugarMethods'][name] = { instance: instance, method: method, original: original };
|
26
|
-
});
|
27
|
-
}
|
28
|
-
|
29
|
-
function initializeClass(klass) {
|
30
|
-
if(klass.SugarMethods) return;
|
31
|
-
defineProperty(klass, 'SugarMethods', {});
|
32
|
-
extend(klass, false, false, {
|
33
|
-
'restore': function() {
|
34
|
-
var all = arguments.length === 0, methods = multiArgs(arguments);
|
35
|
-
iterateOverObject(klass['SugarMethods'], function(name, m) {
|
36
|
-
if(all || methods.has(name)) {
|
37
|
-
defineProperty(m.instance ? klass.prototype : klass, name, m.method);
|
38
|
-
}
|
39
|
-
});
|
40
|
-
},
|
41
|
-
'extend': function(methods, override, instance) {
|
42
|
-
if(klass === object && arguments.length === 0) {
|
43
|
-
mapObjectPrototypeMethods();
|
44
|
-
} else {
|
45
|
-
extend(klass, instance !== false, override, methods);
|
46
|
-
}
|
47
|
-
}
|
48
|
-
});
|
49
|
-
}
|
50
|
-
|
51
|
-
function wrapNative(nativeFn, extendedFn, condition) {
|
52
|
-
return function() {
|
53
|
-
if(nativeFn && (condition === true || !condition.apply(this, arguments))) {
|
54
|
-
return nativeFn.apply(this, arguments);
|
55
|
-
} else {
|
56
|
-
return extendedFn.apply(this, arguments);
|
57
|
-
}
|
58
|
-
}
|
59
|
-
}
|
60
|
-
|
61
|
-
function defineProperty(target, name, method) {
|
62
|
-
if(definePropertySupport) {
|
63
|
-
object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
|
64
|
-
} else {
|
65
|
-
target[name] = method;
|
66
|
-
}
|
67
|
-
}
|
68
|
-
|
69
|
-
// Object helpers
|
70
|
-
|
71
|
-
function hasOwnProperty(obj, key) {
|
72
|
-
return object.prototype.hasOwnProperty.call(obj, key);
|
73
|
-
}
|
74
|
-
|
75
|
-
function iterateOverObject(obj, fn) {
|
76
|
-
var key;
|
77
|
-
for(key in obj) {
|
78
|
-
if(!hasOwnProperty(obj, key)) continue;
|
79
|
-
fn.call(obj, key, obj[key]);
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
function multiMatch(el, match, scope, params) {
|
84
|
-
var result = true;
|
85
|
-
if(el === match) {
|
86
|
-
// Match strictly equal values up front.
|
87
|
-
return true;
|
88
|
-
} else if(object.isRegExp(match)) {
|
89
|
-
// Match against a regexp
|
90
|
-
return regexp(match).test(el);
|
91
|
-
} else if(object.isFunction(match)) {
|
92
|
-
// Match against a filtering function
|
93
|
-
return match.apply(scope, [el].concat(params));
|
94
|
-
} else if(object.isObject(match) && object.isObject(el)) {
|
95
|
-
// Match against a hash or array.
|
96
|
-
iterateOverObject(match, function(key, value) {
|
97
|
-
if(!multiMatch(el[key], match[key], scope, params)) {
|
98
|
-
result = false;
|
99
|
-
}
|
100
|
-
});
|
101
|
-
return !object.isEmpty(match) && result;
|
102
|
-
} else {
|
103
|
-
return object.equal(el, match);
|
104
|
-
}
|
105
|
-
}
|
106
|
-
|
107
|
-
function stringify(thing, stack) {
|
108
|
-
var value, klass, isObject, isArray, arr, i, key, type = typeof thing;
|
109
|
-
|
110
|
-
// Return quickly if string to save cycles
|
111
|
-
if(type === 'string') return thing;
|
112
|
-
|
113
|
-
klass = object.prototype.toString.call(thing)
|
114
|
-
isObject = klass === '[object Object]';
|
115
|
-
isArray = klass === '[object Array]';
|
116
|
-
|
117
|
-
if(thing != null && isObject || isArray) {
|
118
|
-
// This method for checking for cyclic structures was egregiously stolen from
|
119
|
-
// the ingenious method by @kitcambridge from the Underscore script:
|
120
|
-
// https://github.com/documentcloud/underscore/issues/240
|
121
|
-
if(!stack) stack = [];
|
122
|
-
// Allowing a step into the structure before triggering this
|
123
|
-
// script to save cycles on standard JSON structures and also to
|
124
|
-
// try as hard as possible to catch basic properties that may have
|
125
|
-
// been modified.
|
126
|
-
if(stack.length > 1) {
|
127
|
-
i = stack.length;
|
128
|
-
while (i--) {
|
129
|
-
if (stack[i] === thing) {
|
130
|
-
return 'CYC';
|
131
|
-
}
|
132
|
-
}
|
133
|
-
}
|
134
|
-
stack.push(thing);
|
135
|
-
value = string(thing.constructor);
|
136
|
-
arr = isArray ? thing : object.keys(thing).sort();
|
137
|
-
for(i = 0; i < arr.length; i++) {
|
138
|
-
key = isArray ? i : arr[i];
|
139
|
-
value += key + stringify(thing[key], stack);
|
140
|
-
}
|
141
|
-
stack.pop();
|
142
|
-
} else if(1 / thing === -Infinity) {
|
143
|
-
value = '-0';
|
144
|
-
} else {
|
145
|
-
value = string(thing);
|
146
|
-
}
|
147
|
-
return type + klass + value;
|
148
|
-
}
|
149
|
-
|
150
|
-
|
151
|
-
// Argument helpers
|
152
|
-
|
153
|
-
function transformArgument(el, map, context, mapArgs) {
|
154
|
-
if(isUndefined(map)) {
|
155
|
-
return el;
|
156
|
-
} else if(object.isFunction(map)) {
|
157
|
-
return map.apply(context, mapArgs || []);
|
158
|
-
} else if(object.isFunction(el[map])) {
|
159
|
-
return el[map].call(el);
|
160
|
-
} else {
|
161
|
-
return el[map];
|
162
|
-
}
|
163
|
-
}
|
164
|
-
|
165
|
-
function getArgs(args, index) {
|
166
|
-
return Array.prototype.slice.call(args, index);
|
167
|
-
}
|
168
|
-
|
169
|
-
function multiArgs(args, fn, flatten, index) {
|
170
|
-
args = getArgs(args);
|
171
|
-
if(flatten === true) args = arrayFlatten(args, 1);
|
172
|
-
arrayEach(args, fn || function(){}, index);
|
173
|
-
return args;
|
174
|
-
}
|
175
|
-
|
176
|
-
|
177
|
-
// Used for both arrays and strings
|
178
|
-
|
179
|
-
function entryAtIndex(arr, args, str) {
|
180
|
-
var result = [], length = arr.length, loop = args[args.length - 1] !== false, r;
|
181
|
-
multiArgs(args, function(index) {
|
182
|
-
if(object.isBoolean(index)) return false;
|
183
|
-
if(loop) {
|
184
|
-
index = index % length;
|
185
|
-
if(index < 0) index = length + index;
|
186
|
-
}
|
187
|
-
r = str ? arr.charAt(index) || '' : arr[index];
|
188
|
-
result.push(r);
|
189
|
-
});
|
190
|
-
return result.length < 2 ? result[0] : result;
|
191
|
-
}
|
192
|
-
|
193
|
-
/***
|
194
|
-
* Object module
|
195
|
-
*
|
196
|
-
* Much thanks to kangax for his informative aricle about how problems with instanceof and constructor
|
197
|
-
* http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
|
198
|
-
*
|
199
|
-
***/
|
200
|
-
|
201
|
-
function isClass(obj, str) {
|
202
|
-
return object.prototype.toString.call(obj) === '[object '+str+']';
|
203
|
-
}
|
204
|
-
|
205
|
-
function isUndefined(o) {
|
206
|
-
return o === Undefined;
|
207
|
-
}
|
208
|
-
|
209
|
-
function setParamsObject(obj, param, value, deep) {
|
210
|
-
var reg = /^(.+?)(\[.*\])$/, isArray, match, allKeys, key;
|
211
|
-
if(deep !== false && (match = param.match(reg))) {
|
212
|
-
key = match[1];
|
213
|
-
allKeys = match[2].replace(/^\[|\]$/g, '').split('][');
|
214
|
-
arrayEach(allKeys, function(k) {
|
215
|
-
isArray = !k || k.match(/^\d+$/);
|
216
|
-
if(!key && object.isArray(obj)) key = obj.length;
|
217
|
-
if(!obj[key]) {
|
218
|
-
obj[key] = isArray ? [] : {};
|
219
|
-
}
|
220
|
-
obj = obj[key];
|
221
|
-
key = k;
|
222
|
-
});
|
223
|
-
if(!key && isArray) key = obj.length.toString();
|
224
|
-
setParamsObject(obj, key, value);
|
225
|
-
} else if(value.match(/^[\d.]+$/)) {
|
226
|
-
obj[param] = parseFloat(value);
|
227
|
-
} else if(value === 'true') {
|
228
|
-
obj[param] = true;
|
229
|
-
} else if(value === 'false') {
|
230
|
-
obj[param] = false;
|
231
|
-
} else {
|
232
|
-
obj[param] = value;
|
233
|
-
}
|
234
|
-
}
|
235
|
-
|
236
|
-
function Hash(obj) {
|
237
|
-
var self = this;
|
238
|
-
iterateOverObject(obj, function(key, value) {
|
239
|
-
self[key] = value;
|
240
|
-
});
|
241
|
-
}
|
242
|
-
|
243
|
-
/***
|
244
|
-
* @method is[Type](<obj>)
|
245
|
-
* @returns Boolean
|
246
|
-
* @short Returns true if <obj> is an object of that type.
|
247
|
-
* @extra %isObject% will return false on anything that is not an object literal, including instances of inherited classes. Note also that %isNaN% will ONLY return true if the object IS %NaN%. It does not mean the same as browser native %isNaN%, which returns true for anything that is "not a number". Type methods are available as instance methods on extended objects.
|
248
|
-
* @example
|
249
|
-
*
|
250
|
-
* Object.isArray([1,2,3]) -> true
|
251
|
-
* Object.isDate(3) -> false
|
252
|
-
* Object.isRegExp(/wasabi/) -> true
|
253
|
-
* Object.isObject({ broken:'wear' }) -> true
|
254
|
-
*
|
255
|
-
***
|
256
|
-
* @method isArray()
|
257
|
-
* @set isType
|
258
|
-
***
|
259
|
-
* @method isBoolean()
|
260
|
-
* @set isType
|
261
|
-
***
|
262
|
-
* @method isDate()
|
263
|
-
* @set isType
|
264
|
-
***
|
265
|
-
* @method isFunction()
|
266
|
-
* @set isType
|
267
|
-
***
|
268
|
-
* @method isNumber()
|
269
|
-
* @set isType
|
270
|
-
***
|
271
|
-
* @method isString()
|
272
|
-
* @set isType
|
273
|
-
***
|
274
|
-
* @method isRegExp()
|
275
|
-
* @set isType
|
276
|
-
***/
|
277
|
-
|
278
|
-
|
279
|
-
var ObjectTypeMethods = ['isObject','isNaN'];
|
280
|
-
var ObjectHashMethods = ['keys','values','each','merge','isEmpty','clone','equal','watch','tap','has']
|
281
|
-
|
282
|
-
function buildTypeMethods() {
|
283
|
-
var methods = {}, name;
|
284
|
-
arrayEach(['Array','Boolean','Date','Function','Number','String','RegExp'], function(type) {
|
285
|
-
name = 'is' + type;
|
286
|
-
ObjectTypeMethods.push(name);
|
287
|
-
methods[name] = function(obj) {
|
288
|
-
return isClass(obj, type);
|
289
|
-
}
|
290
|
-
});
|
291
|
-
extend(Object, false, false, methods);
|
292
|
-
}
|
293
|
-
|
294
|
-
function buildInstanceMethods(set, target) {
|
295
|
-
var methods = {};
|
296
|
-
arrayEach(set, function(name) {
|
297
|
-
methods[name + (name === 'equal' ? 's' : '')] = function() {
|
298
|
-
return Object[name].apply(null, [this].concat(getArgs(arguments)));
|
299
|
-
}
|
300
|
-
});
|
301
|
-
extend(target, true, false, methods);
|
302
|
-
}
|
303
|
-
|
304
|
-
function buildObject() {
|
305
|
-
buildTypeMethods();
|
306
|
-
buildInstanceMethods(ObjectHashMethods, Hash);
|
307
|
-
}
|
308
|
-
|
309
|
-
function mapObjectPrototypeMethods() {
|
310
|
-
buildInstanceMethods(ObjectTypeMethods.concat(ObjectHashMethods), Object);
|
311
|
-
}
|
312
|
-
|
313
|
-
extend(object, false, true, {
|
314
|
-
/***
|
315
|
-
* @method watch(<obj>, <prop>, <fn>)
|
316
|
-
* @returns Nothing
|
317
|
-
* @short Watches a property of <obj> and runs <fn> when it changes.
|
318
|
-
* @extra <fn> is passed three arguments: the property <prop>, the old value, and the new value. The return value of [fn] will be set as the new value. This method is useful for things such as validating or cleaning the value when it is set. Warning: this method WILL NOT work in browsers that don't support %Object.defineProperty%. This notably includes IE 8 and below, and Opera. This is the only method in Sugar that is not fully compatible with all browsers. %watch% is available as an instance method on extended objects.
|
319
|
-
* @example
|
320
|
-
*
|
321
|
-
* Object.watch({ foo: 'bar' }, 'foo', function(prop, oldVal, newVal) {
|
322
|
-
* // Will be run when the property 'foo' is set on the object.
|
323
|
-
* });
|
324
|
-
* Object.extended().watch({ foo: 'bar' }, 'foo', function(prop, oldVal, newVal) {
|
325
|
-
* // Will be run when the property 'foo' is set on the object.
|
326
|
-
* });
|
327
|
-
*
|
328
|
-
***/
|
329
|
-
'watch': function(obj, prop, fn) {
|
330
|
-
if(!definePropertySupport) return;
|
331
|
-
var value = obj[prop];
|
332
|
-
object.defineProperty(obj, prop, {
|
333
|
-
'get': function() {
|
334
|
-
return value;
|
335
|
-
},
|
336
|
-
'set': function(to) {
|
337
|
-
value = fn.call(obj, prop, value, to);
|
338
|
-
},
|
339
|
-
'enumerable': true,
|
340
|
-
'configurable': true
|
341
|
-
});
|
342
|
-
}
|
343
|
-
});
|
344
|
-
|
345
|
-
extend(object, false, false, {
|
346
|
-
|
347
|
-
/***
|
348
|
-
* @method Object.extended(<obj> = {})
|
349
|
-
* @returns Extended object
|
350
|
-
* @short Creates a new object, equivalent to %new Object()% or %{}%, but with extended methods.
|
351
|
-
* @extra See extended objects for more.
|
352
|
-
* @example
|
353
|
-
*
|
354
|
-
* Object.extended()
|
355
|
-
* Object.extended({ happy:true, pappy:false }).keys() -> ['happy','pappy']
|
356
|
-
* Object.extended({ happy:true, pappy:false }).values() -> [true, false]
|
357
|
-
*
|
358
|
-
***/
|
359
|
-
'extended': function(obj) {
|
360
|
-
return new Hash(obj);
|
361
|
-
},
|
362
|
-
|
363
|
-
/***
|
364
|
-
* @method isObject()
|
365
|
-
* @set isType
|
366
|
-
***/
|
367
|
-
'isObject': function(obj) {
|
368
|
-
if(obj == null) {
|
369
|
-
return false;
|
370
|
-
} else {
|
371
|
-
// === on the constructor is not safe across iframes
|
372
|
-
return isClass(obj, 'Object') && string(obj.constructor) === string(object) || obj.constructor === Hash;
|
373
|
-
}
|
374
|
-
},
|
375
|
-
|
376
|
-
/***
|
377
|
-
* @method isNaN()
|
378
|
-
* @set isType
|
379
|
-
***/
|
380
|
-
'isNaN': function(obj) {
|
381
|
-
// This is only true of NaN
|
382
|
-
return object.isNumber(obj) && obj.valueOf() !== obj.valueOf();
|
383
|
-
},
|
384
|
-
|
385
|
-
/***
|
386
|
-
* @method each(<obj>, [fn])
|
387
|
-
* @returns Object
|
388
|
-
* @short Iterates over each property in <obj> calling [fn] on each iteration.
|
389
|
-
* @extra %each% is available as an instance method on extended objects.
|
390
|
-
* @example
|
391
|
-
*
|
392
|
-
* Object.each({ broken:'wear' }, function(key, value) {
|
393
|
-
* // Iterates over each key/value pair.
|
394
|
-
* });
|
395
|
-
* Object.extended({ broken:'wear' }).each(function(key, value) {
|
396
|
-
* // Iterates over each key/value pair.
|
397
|
-
* });
|
398
|
-
*
|
399
|
-
***/
|
400
|
-
'each': function(obj, fn) {
|
401
|
-
if(fn) {
|
402
|
-
iterateOverObject(obj, function(k,v) {
|
403
|
-
fn.call(obj, k, v, obj);
|
404
|
-
});
|
405
|
-
}
|
406
|
-
return obj;
|
407
|
-
},
|
408
|
-
|
409
|
-
/***
|
410
|
-
* @method merge(<target>, <source>, [deep] = false, [resolve] = true)
|
411
|
-
* @returns Merged object
|
412
|
-
* @short Merges all the properties of <source> into <target>.
|
413
|
-
* @extra Merges are shallow unless [deep] is %true%. Properties of <source> will win in the case of conflicts, unless [resolve] is %false%. [resolve] can also be a function that resolves the conflict. In this case it will be passed 3 arguments, %key%, %targetVal%, and %sourceVal%, with the context set to <source>. This will allow you to solve conflict any way you want, ie. adding two numbers together, etc. %merge% is available as an instance method on extended objects.
|
414
|
-
* @example
|
415
|
-
*
|
416
|
-
* Object.merge({a:1},{b:2}) -> { a:1, b:2 }
|
417
|
-
* Object.merge({a:1},{a:2}, false, false) -> { a:1 }
|
418
|
-
+ Object.merge({a:1},{a:2}, false, function(key, a, b) {
|
419
|
-
* return a + b;
|
420
|
-
* }); -> { a:3 }
|
421
|
-
* Object.extended({a:1}).merge({b:2}) -> { a:1, b:2 }
|
422
|
-
*
|
423
|
-
***/
|
424
|
-
'merge': function(target, source, deep, resolve) {
|
425
|
-
var key, val;
|
426
|
-
// Strings cannot be reliably merged thanks to
|
427
|
-
// their properties not being enumerable in < IE8.
|
428
|
-
if(target && typeof source != 'string') {
|
429
|
-
for(key in source) {
|
430
|
-
if(!hasOwnProperty(source, key) || !target) continue;
|
431
|
-
val = source[key];
|
432
|
-
// Conflict!
|
433
|
-
if(target[key] !== Undefined) {
|
434
|
-
// Do not merge.
|
435
|
-
if(resolve === false) {
|
436
|
-
continue;
|
437
|
-
}
|
438
|
-
// Use the result of the callback as the result.
|
439
|
-
if(object.isFunction(resolve)) {
|
440
|
-
val = resolve.call(source, key, target[key], source[key])
|
441
|
-
}
|
442
|
-
}
|
443
|
-
// Deep merging.
|
444
|
-
if(deep === true && val && typeof val === 'object') {
|
445
|
-
if(object.isDate(val)) {
|
446
|
-
val = new Date(val.getTime());
|
447
|
-
} else if(object.isRegExp(val)) {
|
448
|
-
val = new RegExp(val.source, val.getFlags());
|
449
|
-
} else {
|
450
|
-
if(!target[key]) target[key] = array.isArray(val) ? [] : {};
|
451
|
-
Object.merge(target[key], source[key], deep, resolve);
|
452
|
-
continue;
|
453
|
-
}
|
454
|
-
}
|
455
|
-
target[key] = val;
|
456
|
-
}
|
457
|
-
}
|
458
|
-
return target;
|
459
|
-
},
|
460
|
-
|
461
|
-
/***
|
462
|
-
* @method isEmpty(<obj>)
|
463
|
-
* @returns Boolean
|
464
|
-
* @short Returns true if <obj> is empty.
|
465
|
-
* @extra %isEmpty% is available as an instance method on extended objects.
|
466
|
-
* @example
|
467
|
-
*
|
468
|
-
* Object.isEmpty({}) -> true
|
469
|
-
* Object.isEmpty({foo:'bar'}) -> false
|
470
|
-
* Object.extended({foo:'bar'}).isEmpty() -> false
|
471
|
-
*
|
472
|
-
***/
|
473
|
-
'isEmpty': function(obj) {
|
474
|
-
if(obj == null || typeof obj != 'object') return !(obj && obj.length > 0);
|
475
|
-
return object.keys(obj).length == 0;
|
476
|
-
},
|
477
|
-
|
478
|
-
/***
|
479
|
-
* @method equal(<a>, <b>)
|
480
|
-
* @returns Boolean
|
481
|
-
* @short Returns true if <a> and <b> are equal.
|
482
|
-
* @extra %equal% in Sugar is "egal", meaning the values are equal if they are "not observably distinguishable". Note that on extended objects the name is %equals% for readability.
|
483
|
-
* @example
|
484
|
-
*
|
485
|
-
* Object.equal({a:2}, {a:2}) -> true
|
486
|
-
* Object.equal({a:2}, {a:3}) -> false
|
487
|
-
* Object.extended({a:2}).equals({a:3}) -> false
|
488
|
-
*
|
489
|
-
***/
|
490
|
-
'equal': function(a, b) {
|
491
|
-
return stringify(a) === stringify(b);
|
492
|
-
},
|
493
|
-
|
494
|
-
/***
|
495
|
-
* @method values(<obj>, [fn])
|
496
|
-
* @returns Array
|
497
|
-
* @short Returns an array containing the values in <obj>. Optionally calls [fn] for each value.
|
498
|
-
* @extra Returned values are in no particular order. %values% is available as an instance method on extended objects.
|
499
|
-
* @example
|
500
|
-
*
|
501
|
-
* Object.values({ broken: 'wear' }) -> ['wear']
|
502
|
-
* Object.values({ broken: 'wear' }, function(value) {
|
503
|
-
* // Called once for each value.
|
504
|
-
* });
|
505
|
-
* Object.extended({ broken: 'wear' }).values() -> ['wear']
|
506
|
-
*
|
507
|
-
***/
|
508
|
-
'values': function(obj, fn) {
|
509
|
-
var values = [];
|
510
|
-
iterateOverObject(obj, function(k,v) {
|
511
|
-
values.push(v);
|
512
|
-
if(fn) fn.call(obj,v);
|
513
|
-
});
|
514
|
-
return values;
|
515
|
-
},
|
516
|
-
|
517
|
-
/***
|
518
|
-
* @method clone(<obj> = {}, [deep] = false)
|
519
|
-
* @returns Cloned object
|
520
|
-
* @short Creates a clone (copy) of <obj>.
|
521
|
-
* @extra Default is a shallow clone, unless [deep] is true. %clone% is available as an instance method on extended objects.
|
522
|
-
* @example
|
523
|
-
*
|
524
|
-
* Object.clone({foo:'bar'}) -> { foo: 'bar' }
|
525
|
-
* Object.clone() -> {}
|
526
|
-
* Object.extended({foo:'bar'}).clone() -> { foo: 'bar' }
|
527
|
-
*
|
528
|
-
***/
|
529
|
-
'clone': function(obj, deep) {
|
530
|
-
if(obj == null || typeof obj !== 'object') return obj;
|
531
|
-
if(array.isArray(obj)) return obj.clone();
|
532
|
-
var target = obj.constructor === Hash ? new Hash() : {};
|
533
|
-
return object.merge(target, obj, deep);
|
534
|
-
},
|
535
|
-
|
536
|
-
/***
|
537
|
-
* @method Object.fromQueryString(<str>, [deep] = true)
|
538
|
-
* @returns Object
|
539
|
-
* @short Converts the query string of a URL into an object.
|
540
|
-
* @extra If [deep] is %false%, conversion will only accept shallow params (ie. no object or arrays with %[]% syntax) as these are not universally supported.
|
541
|
-
* @example
|
542
|
-
*
|
543
|
-
* Object.fromQueryString('foo=bar&broken=wear') -> { foo: 'bar', broken: 'wear' }
|
544
|
-
* Object.fromQueryString('foo[]=1&foo[]=2') -> { foo: [1,2] }
|
545
|
-
*
|
546
|
-
***/
|
547
|
-
'fromQueryString': function(str, deep) {
|
548
|
-
var result = object.extended(), split;
|
549
|
-
str = str && str.toString ? str.toString() : '';
|
550
|
-
str.replace(/^.*?\?/, '').unescapeURL().split('&').each(function(p) {
|
551
|
-
var split = p.split('=');
|
552
|
-
if(split.length !== 2) return;
|
553
|
-
setParamsObject(result, split[0], split[1], deep);
|
554
|
-
});
|
555
|
-
return result;
|
556
|
-
},
|
557
|
-
|
558
|
-
/***
|
559
|
-
* @method tap(<obj>, <fn>)
|
560
|
-
* @returns Object
|
561
|
-
* @short Runs <fn> and returns <obj>.
|
562
|
-
* @extra A string can also be used as a shortcut to a method. This method is used to run an intermediary function in the middle of method chaining. As a standalone method on the Object class it doesn't have too much use. The power of %tap% comes when using extended objects or modifying the Object prototype with Object.extend().
|
563
|
-
* @example
|
564
|
-
*
|
565
|
-
* Object.extend();
|
566
|
-
* [2,4,6].map(Math.exp).tap(function(){ arr.pop(); }).map(Math.round); -> [7,55]
|
567
|
-
* [2,4,6].map(Math.exp).tap('pop').map(Math.round); -> [7,55]
|
568
|
-
*
|
569
|
-
***/
|
570
|
-
'tap': function(obj, fn) {
|
571
|
-
transformArgument(obj, fn, obj, [obj]);
|
572
|
-
return obj;
|
573
|
-
},
|
574
|
-
|
575
|
-
/***
|
576
|
-
* @method has(<obj>, <key>)
|
577
|
-
* @returns Boolean
|
578
|
-
* @short Checks if <obj> has <key> using hasOwnProperty from Object.prototype.
|
579
|
-
* @extra This method is considered safer than %Object#hasOwnProperty% when using objects as hashes. See %http://www.devthought.com/2012/01/18/an-object-is-not-a-hash/% for more.
|
580
|
-
* @example
|
581
|
-
*
|
582
|
-
* Object.has({ foo: 'bar' }, 'foo') -> true
|
583
|
-
* Object.has({ foo: 'bar' }, 'baz') -> false
|
584
|
-
* Object.has({ hasOwnProperty: true }, 'foo') -> false
|
585
|
-
***/
|
586
|
-
'has': function (obj, key) {
|
587
|
-
return hasOwnProperty(obj, key);
|
588
|
-
}
|
589
|
-
|
590
|
-
});
|
591
|
-
|
592
|
-
|
593
|
-
extend(object, false, function() { return arguments.length > 1; }, {
|
594
|
-
|
595
|
-
/***
|
596
|
-
* @method keys(<obj>, [fn])
|
597
|
-
* @returns Array
|
598
|
-
* @short Returns an array containing the keys in <obj>. Optionally calls [fn] for each key.
|
599
|
-
* @extra This method is provided for browsers that don't support it natively, and additionally is enhanced to accept the callback [fn]. Returned keys are in no particular order. %keys% is available as an instance method on extended objects.
|
600
|
-
* @example
|
601
|
-
*
|
602
|
-
* Object.keys({ broken: 'wear' }) -> ['broken']
|
603
|
-
* Object.keys({ broken: 'wear' }, function(key, value) {
|
604
|
-
* // Called once for each key.
|
605
|
-
* });
|
606
|
-
* Object.extended({ broken: 'wear' }).keys() -> ['broken']
|
607
|
-
*
|
608
|
-
***/
|
609
|
-
'keys': function(obj, fn) {
|
610
|
-
if(obj == null || typeof obj != 'object' && !object.isRegExp(obj) && !object.isFunction(obj)) {
|
611
|
-
throw new TypeError('Object required');
|
612
|
-
}
|
613
|
-
var keys = [];
|
614
|
-
iterateOverObject(obj, function(key, value) {
|
615
|
-
keys.push(key);
|
616
|
-
if(fn) fn.call(obj, key, value);
|
617
|
-
});
|
618
|
-
return keys;
|
619
|
-
}
|
620
|
-
|
621
|
-
});
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
/***
|
631
|
-
* Array module
|
632
|
-
*
|
633
|
-
***/
|
634
|
-
|
635
|
-
|
636
|
-
// Basic array internal methods
|
637
|
-
|
638
|
-
function arrayEach(arr, fn, startIndex, loop, sparse) {
|
639
|
-
var length, index, i;
|
640
|
-
checkCallback(fn);
|
641
|
-
if(startIndex < 0) startIndex = arr.length + startIndex;
|
642
|
-
i = toIntegerWithDefault(startIndex, 0);
|
643
|
-
length = loop === true ? arr.length + i : arr.length;
|
644
|
-
while(i < length) {
|
645
|
-
index = i % arr.length;
|
646
|
-
if(!(index in arr) && sparse === true) {
|
647
|
-
return iterateOverSparseArray(arr, fn, i, loop);
|
648
|
-
} else if(fn.call(arr, arr[index], index, arr) === false) {
|
649
|
-
break;
|
650
|
-
}
|
651
|
-
i++;
|
652
|
-
}
|
653
|
-
}
|
654
|
-
|
655
|
-
function arrayFind(arr, f, startIndex, loop, returnIndex) {
|
656
|
-
var result, index;
|
657
|
-
arrayEach(arr, function(el, i, arr) {
|
658
|
-
if(multiMatch(el, f, arr, [i, arr])) {
|
659
|
-
result = el;
|
660
|
-
index = i;
|
661
|
-
return false;
|
662
|
-
}
|
663
|
-
}, startIndex, loop);
|
664
|
-
return returnIndex ? index : result;
|
665
|
-
}
|
666
|
-
|
667
|
-
function arrayUnique(arr, map) {
|
668
|
-
var result = [], o = {}, stringified, transformed;
|
669
|
-
arrayEach(arr, function(el, i) {
|
670
|
-
transformed = map ? transformArgument(el, map, arr, [el, i, arr]) : el;
|
671
|
-
stringified = stringify(transformed);
|
672
|
-
if(!arrayObjectExists(o, stringified, el)) {
|
673
|
-
o[stringified] = transformed;
|
674
|
-
result.push(el);
|
675
|
-
}
|
676
|
-
})
|
677
|
-
return result;
|
678
|
-
}
|
679
|
-
|
680
|
-
function arrayFlatten(arr, level, current) {
|
681
|
-
level = level || Infinity;
|
682
|
-
current = current || 0;
|
683
|
-
var result = [];
|
684
|
-
arrayEach(arr, function(el) {
|
685
|
-
if(object.isArray(el) && current < level) {
|
686
|
-
result = result.concat(arrayFlatten(el, level, current + 1));
|
687
|
-
} else {
|
688
|
-
result.push(el);
|
689
|
-
}
|
690
|
-
});
|
691
|
-
return result;
|
692
|
-
}
|
693
|
-
|
694
|
-
function arrayIntersect(arr1, arr2, subtract) {
|
695
|
-
var result = [], o = {};
|
696
|
-
arr2.each(function(el) {
|
697
|
-
o[stringify(el)] = el;
|
698
|
-
});
|
699
|
-
arr1.each(function(el) {
|
700
|
-
var stringified = stringify(el), exists = arrayObjectExists(o, stringified, el);
|
701
|
-
// Add the result to the array if:
|
702
|
-
// 1. We're subtracting intersections or it doesn't already exist in the result and
|
703
|
-
// 2. It exists in the compared array and we're adding, or it doesn't exist and we're removing.
|
704
|
-
if(exists != subtract) {
|
705
|
-
delete o[stringified];
|
706
|
-
result.push(el);
|
707
|
-
}
|
708
|
-
});
|
709
|
-
return result;
|
710
|
-
}
|
711
|
-
|
712
|
-
function arrayObjectExists(hash, stringified, obj) {
|
713
|
-
return stringified in hash && (typeof obj !== 'function' || obj === hash[stringified]);
|
714
|
-
}
|
715
|
-
|
716
|
-
// ECMA5 methods
|
717
|
-
|
718
|
-
function arrayIndexOf(arr, search, fromIndex, increment) {
|
719
|
-
var length = arr.length,
|
720
|
-
fromRight = increment == -1,
|
721
|
-
start = fromRight ? length - 1 : 0,
|
722
|
-
index = toIntegerWithDefault(fromIndex, start);
|
723
|
-
if(index < 0) {
|
724
|
-
index = length + index;
|
725
|
-
}
|
726
|
-
if((!fromRight && index < 0) || (fromRight && index >= length)) {
|
727
|
-
index = start;
|
728
|
-
}
|
729
|
-
while((fromRight && index >= 0) || (!fromRight && index < length)) {
|
730
|
-
if(arr[index] === search) {
|
731
|
-
return index;
|
732
|
-
}
|
733
|
-
index += increment;
|
734
|
-
}
|
735
|
-
return -1;
|
736
|
-
}
|
737
|
-
|
738
|
-
function arrayReduce(arr, fn, initialValue, fromRight) {
|
739
|
-
var length = arr.length, count = 0, defined = initialValue !== Undefined, result, index;
|
740
|
-
checkCallback(fn);
|
741
|
-
if(length == 0 && !defined) {
|
742
|
-
throw new TypeError('Reduce called on empty array with no initial value');
|
743
|
-
} else if(defined) {
|
744
|
-
result = initialValue;
|
745
|
-
} else {
|
746
|
-
result = arr[fromRight ? length - 1 : count];
|
747
|
-
count++;
|
748
|
-
}
|
749
|
-
while(count < length) {
|
750
|
-
index = fromRight ? length - count - 1 : count;
|
751
|
-
if(index in arr) {
|
752
|
-
result = fn.call(Undefined, result, arr[index], index, arr);
|
753
|
-
}
|
754
|
-
count++;
|
755
|
-
}
|
756
|
-
return result;
|
757
|
-
}
|
758
|
-
|
759
|
-
function toIntegerWithDefault(i, d) {
|
760
|
-
if(isNaN(i)) {
|
761
|
-
return d;
|
762
|
-
} else {
|
763
|
-
return parseInt(i >> 0);
|
764
|
-
}
|
765
|
-
}
|
766
|
-
|
767
|
-
function isArrayIndex(arr, i) {
|
768
|
-
return i in arr && toUInt32(i) == i && i != 0xffffffff;
|
769
|
-
}
|
770
|
-
|
771
|
-
function toUInt32(i) {
|
772
|
-
return i >>> 0;
|
773
|
-
}
|
774
|
-
|
775
|
-
function checkCallback(fn) {
|
776
|
-
if(!fn || !fn.call) {
|
777
|
-
throw new TypeError('Callback is not callable');
|
778
|
-
}
|
779
|
-
}
|
780
|
-
|
781
|
-
function checkFirstArgumentExists(args) {
|
782
|
-
if(args.length === 0) {
|
783
|
-
throw new TypeError('First argument must be defined');
|
784
|
-
}
|
785
|
-
}
|
786
|
-
|
787
|
-
// Support methods
|
788
|
-
|
789
|
-
function iterateOverSparseArray(arr, fn, fromIndex, loop) {
|
790
|
-
var indexes = [], i;
|
791
|
-
for(i in arr) {
|
792
|
-
if(isArrayIndex(arr, i) && i >= fromIndex) {
|
793
|
-
indexes.push(i.toNumber());
|
794
|
-
}
|
795
|
-
}
|
796
|
-
indexes.sort().each(function(index) {
|
797
|
-
return fn.call(arr, arr[index], index, arr);
|
798
|
-
});
|
799
|
-
return arr;
|
800
|
-
}
|
801
|
-
|
802
|
-
function getMinOrMax(obj, map, which, isArray) {
|
803
|
-
var max = which === 'max', min = which === 'min';
|
804
|
-
var edge = max ? -Infinity : Infinity;
|
805
|
-
var result = [];
|
806
|
-
iterateOverObject(obj, function(key) {
|
807
|
-
var entry = obj[key];
|
808
|
-
var test = transformArgument(entry, map, obj, isArray? [entry, key.toNumber(), obj] : []);
|
809
|
-
if(test === edge) {
|
810
|
-
result.push(entry);
|
811
|
-
} else if((max && test > edge) || (min && test < edge)) {
|
812
|
-
result = [entry];
|
813
|
-
edge = test;
|
814
|
-
}
|
815
|
-
});
|
816
|
-
return result;
|
817
|
-
}
|
818
|
-
|
819
|
-
|
820
|
-
// Alphanumeric collation helpers
|
821
|
-
|
822
|
-
function collateStrings(a, b) {
|
823
|
-
var aValue, bValue, aChar, bChar, aEquiv, bEquiv, index = 0, tiebreaker = 0;
|
824
|
-
a = getCollationReadyString(a);
|
825
|
-
b = getCollationReadyString(b);
|
826
|
-
do {
|
827
|
-
aChar = getCollationCharacter(a, index);
|
828
|
-
bChar = getCollationCharacter(b, index);
|
829
|
-
aValue = getCollationValue(aChar);
|
830
|
-
bValue = getCollationValue(bChar);
|
831
|
-
if(aValue === -1 || bValue === -1) {
|
832
|
-
aValue = a.charCodeAt(index) || null;
|
833
|
-
bValue = b.charCodeAt(index) || null;
|
834
|
-
}
|
835
|
-
aEquiv = aChar !== a.charAt(index);
|
836
|
-
bEquiv = bChar !== b.charAt(index);
|
837
|
-
if(aEquiv !== bEquiv && tiebreaker === 0) {
|
838
|
-
tiebreaker = aEquiv - bEquiv;
|
839
|
-
}
|
840
|
-
index += 1;
|
841
|
-
} while(aValue != null && bValue != null && aValue === bValue);
|
842
|
-
if(aValue === bValue) return tiebreaker;
|
843
|
-
return aValue < bValue ? -1 : 1;
|
844
|
-
}
|
845
|
-
|
846
|
-
function getCollationReadyString(str) {
|
847
|
-
if(array[AlphanumericSortIgnoreCase]) {
|
848
|
-
str = str.toLowerCase();
|
849
|
-
}
|
850
|
-
return str.remove(array[AlphanumericSortIgnore]);
|
851
|
-
}
|
852
|
-
|
853
|
-
function getCollationCharacter(str, index) {
|
854
|
-
var chr = str.charAt(index), eq = array[AlphanumericSortEquivalents] || {};
|
855
|
-
return eq[chr] || chr;
|
856
|
-
}
|
857
|
-
|
858
|
-
function getCollationValue(chr) {
|
859
|
-
if(!chr) {
|
860
|
-
return null;
|
861
|
-
} else {
|
862
|
-
return array[AlphanumericSortOrder].indexOf(chr);
|
863
|
-
}
|
864
|
-
}
|
865
|
-
|
866
|
-
var AlphanumericSortOrder = 'AlphanumericSortOrder';
|
867
|
-
var AlphanumericSortIgnore = 'AlphanumericSortIgnore';
|
868
|
-
var AlphanumericSortIgnoreCase = 'AlphanumericSortIgnoreCase';
|
869
|
-
var AlphanumericSortEquivalents = 'AlphanumericSortEquivalents';
|
870
|
-
|
871
|
-
function buildArray() {
|
872
|
-
var order = 'AÁÀÂÃĄBCĆČÇDĎÐEÉÈĚÊËĘFGĞHıIÍÌİÎÏJKLŁMNŃŇÑOÓÒÔPQRŘSŚŠŞTŤUÚÙŮÛÜVWXYÝZŹŻŽÞÆŒØÕÅÄÖ';
|
873
|
-
var equiv = 'AÁÀÂÃÄ,CÇ,EÉÈÊË,IÍÌİÎÏ,OÓÒÔÕÖ,Sß,UÚÙÛÜ';
|
874
|
-
array[AlphanumericSortOrder] = order.split('').map(function(str) {
|
875
|
-
return str + str.toLowerCase();
|
876
|
-
}).join('');
|
877
|
-
var equivalents = {};
|
878
|
-
equiv.split(',').each(function(set) {
|
879
|
-
var equivalent = set.charAt(0);
|
880
|
-
set.slice(1).chars(function(chr) {
|
881
|
-
equivalents[chr] = equivalent;
|
882
|
-
equivalents[chr.toLowerCase()] = equivalent.toLowerCase();
|
883
|
-
});
|
884
|
-
});
|
885
|
-
array[AlphanumericSortIgnoreCase] = true;
|
886
|
-
array[AlphanumericSortEquivalents] = equivalents;
|
887
|
-
}
|
888
|
-
|
889
|
-
extend(array, false, false, {
|
890
|
-
|
891
|
-
/***
|
892
|
-
*
|
893
|
-
* @method Array.create(<obj1>, <obj2>, ...)
|
894
|
-
* @returns Array
|
895
|
-
* @short Alternate array constructor.
|
896
|
-
* @extra This method will create a single array by calling %concat% on all arguments passed. In addition to ensuring that an unknown variable is in a single, flat array (the standard constructor will create nested arrays, this one will not), it is also a useful shorthand to convert a function's arguments object into a standard array.
|
897
|
-
* @example
|
898
|
-
*
|
899
|
-
* Array.create('one', true, 3) -> ['one', true, 3]
|
900
|
-
* Array.create(['one', true, 3]) -> ['one', true, 3]
|
901
|
-
+ Array.create(function(n) {
|
902
|
-
* return arguments;
|
903
|
-
* }('howdy', 'doody'));
|
904
|
-
*
|
905
|
-
***/
|
906
|
-
'create': function(obj) {
|
907
|
-
var result = [];
|
908
|
-
multiArgs(arguments, function(a) {
|
909
|
-
if(a && a.callee) a = getArgs(a);
|
910
|
-
result = result.concat(a);
|
911
|
-
});
|
912
|
-
return result;
|
913
|
-
},
|
914
|
-
|
915
|
-
/***
|
916
|
-
*
|
917
|
-
* @method Array.isArray(<obj>)
|
918
|
-
* @returns Boolean
|
919
|
-
* @short Returns true if <obj> is an Array.
|
920
|
-
* @extra This method is provided for browsers that don't support it internally.
|
921
|
-
* @example
|
922
|
-
*
|
923
|
-
* Array.isArray(3) -> false
|
924
|
-
* Array.isArray(true) -> false
|
925
|
-
* Array.isArray('wasabi') -> false
|
926
|
-
* Array.isArray([1,2,3]) -> true
|
927
|
-
*
|
928
|
-
***/
|
929
|
-
'isArray': function(obj) {
|
930
|
-
return isClass(obj, 'Array');
|
931
|
-
}
|
932
|
-
|
933
|
-
});
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
extend(array, true, function() { var a = arguments; return a.length > 0 && !object.isFunction(a[0]); }, {
|
938
|
-
|
939
|
-
/***
|
940
|
-
* @method every(<f>, [scope])
|
941
|
-
* @returns Boolean
|
942
|
-
* @short Returns true if all elements in the array match <f>.
|
943
|
-
* @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>. %all% is provided an alias.
|
944
|
-
* @example
|
945
|
-
*
|
946
|
-
+ ['a','a','a'].every(function(n) {
|
947
|
-
* return n == 'a';
|
948
|
-
* });
|
949
|
-
* ['a','a','a'].every('a') -> true
|
950
|
-
* [{a:2},{a:2}].every({a:2}) -> true
|
951
|
-
*
|
952
|
-
***/
|
953
|
-
'every': function(f, scope) {
|
954
|
-
var length = this.length, index = 0;
|
955
|
-
checkFirstArgumentExists(arguments);
|
956
|
-
while(index < length) {
|
957
|
-
if(index in this && !multiMatch(this[index], f, scope, [index, this])) {
|
958
|
-
return false;
|
959
|
-
}
|
960
|
-
index++;
|
961
|
-
}
|
962
|
-
return true;
|
963
|
-
},
|
964
|
-
|
965
|
-
/***
|
966
|
-
* @method some(<f>, [scope])
|
967
|
-
* @returns Boolean
|
968
|
-
* @short Returns true if any element in the array matches <f>.
|
969
|
-
* @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>. %any% and %has% are provided as aliases.
|
970
|
-
* @example
|
971
|
-
*
|
972
|
-
+ ['a','b','c'].some(function(n) {
|
973
|
-
* return n == 'a';
|
974
|
-
* });
|
975
|
-
+ ['a','b','c'].some(function(n) {
|
976
|
-
* return n == 'd';
|
977
|
-
* });
|
978
|
-
* ['a','b','c'].some('a') -> true
|
979
|
-
* [{a:2},{b:5}].some({a:2}) -> true
|
980
|
-
*
|
981
|
-
***/
|
982
|
-
'some': function(f, scope) {
|
983
|
-
var length = this.length, index = 0;
|
984
|
-
checkFirstArgumentExists(arguments);
|
985
|
-
while(index < length) {
|
986
|
-
if(index in this && multiMatch(this[index], f, scope, [index, this])) {
|
987
|
-
return true;
|
988
|
-
}
|
989
|
-
index++;
|
990
|
-
}
|
991
|
-
return false;
|
992
|
-
},
|
993
|
-
|
994
|
-
/***
|
995
|
-
* @method map(<map>, [scope])
|
996
|
-
* @returns Array
|
997
|
-
* @short Maps the array to another array containing the values that are the result of calling <map> on each element.
|
998
|
-
* @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts a string, which is a shortcut for a function that gets that property (or invokes a function) on each element. %collect% is provided as an alias.
|
999
|
-
* @example
|
1000
|
-
*
|
1001
|
-
+ [1,2,3].map(function(n) {
|
1002
|
-
* return n * 3;
|
1003
|
-
* }); -> [3,6,9]
|
1004
|
-
* ['one','two','three'].map(function(n) {
|
1005
|
-
* return n.length;
|
1006
|
-
* }); -> [3,3,5]
|
1007
|
-
* ['one','two','three'].map('length') -> [3,3,5]
|
1008
|
-
*
|
1009
|
-
***/
|
1010
|
-
'map': function(map, scope) {
|
1011
|
-
var length = this.length, index = 0, el, result = new Array(length);
|
1012
|
-
checkFirstArgumentExists(arguments);
|
1013
|
-
while(index < length) {
|
1014
|
-
if(index in this) {
|
1015
|
-
el = this[index];
|
1016
|
-
result[index] = transformArgument(el, map, scope, [el, index, this]);
|
1017
|
-
}
|
1018
|
-
index++;
|
1019
|
-
}
|
1020
|
-
return result;
|
1021
|
-
},
|
1022
|
-
|
1023
|
-
/***
|
1024
|
-
* @method filter(<f>, [scope])
|
1025
|
-
* @returns Array
|
1026
|
-
* @short Returns any elements in the array that match <f>.
|
1027
|
-
* @extra [scope] is the %this% object. In addition to providing this method for browsers that don't support it natively, this enhanced method also directly accepts strings, numbers, deep objects, and arrays for <f>.
|
1028
|
-
* @example
|
1029
|
-
*
|
1030
|
-
+ [1,2,3].filter(function(n) {
|
1031
|
-
* return n > 1;
|
1032
|
-
* });
|
1033
|
-
* [1,2,2,4].filter(2) -> 2
|
1034
|
-
*
|
1035
|
-
***/
|
1036
|
-
'filter': function(f, scope) {
|
1037
|
-
var length = this.length, index = 0, result = [];
|
1038
|
-
checkFirstArgumentExists(arguments);
|
1039
|
-
while(index < length) {
|
1040
|
-
if(index in this && multiMatch(this[index], f, scope, [index, this])) {
|
1041
|
-
result.push(this[index]);
|
1042
|
-
}
|
1043
|
-
index++;
|
1044
|
-
}
|
1045
|
-
return result;
|
1046
|
-
}
|
1047
|
-
|
1048
|
-
});
|
1049
|
-
|
1050
|
-
|
1051
|
-
extend(array, true, false, {
|
1052
|
-
|
1053
|
-
/***
|
1054
|
-
* @method indexOf(<search>, [fromIndex])
|
1055
|
-
* @returns Number
|
1056
|
-
* @short Searches the array and returns the first index where <search> occurs, or -1 if the element is not found.
|
1057
|
-
* @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>. It does not support enhanced functionality such as searching the contents against a regex, callback, or deep comparison of objects. For such functionality, use the %find% method instead.
|
1058
|
-
* @example
|
1059
|
-
*
|
1060
|
-
* [1,2,3].indexOf(3) -> 1
|
1061
|
-
* [1,2,3].indexOf(7) -> -1
|
1062
|
-
*
|
1063
|
-
***/
|
1064
|
-
'indexOf': function(search, fromIndex) {
|
1065
|
-
if(object.isString(this)) return this.indexOf(search, fromIndex);
|
1066
|
-
return arrayIndexOf(this, search, fromIndex, 1);
|
1067
|
-
},
|
1068
|
-
|
1069
|
-
/***
|
1070
|
-
* @method lastIndexOf(<search>, [fromIndex])
|
1071
|
-
* @returns Number
|
1072
|
-
* @short Searches the array and returns the last index where <search> occurs, or -1 if the element is not found.
|
1073
|
-
* @extra [fromIndex] is the index from which to begin the search. This method performs a simple strict equality comparison on <search>.
|
1074
|
-
* @example
|
1075
|
-
*
|
1076
|
-
* [1,2,1].lastIndexOf(1) -> 2
|
1077
|
-
* [1,2,1].lastIndexOf(7) -> -1
|
1078
|
-
*
|
1079
|
-
***/
|
1080
|
-
'lastIndexOf': function(search, fromIndex) {
|
1081
|
-
if(object.isString(this)) return this.lastIndexOf(search, fromIndex);
|
1082
|
-
return arrayIndexOf(this, search, fromIndex, -1);
|
1083
|
-
},
|
1084
|
-
|
1085
|
-
/***
|
1086
|
-
* @method forEach([fn], [scope])
|
1087
|
-
* @returns Nothing
|
1088
|
-
* @short Iterates over the array, calling [fn] on each loop.
|
1089
|
-
* @extra This method is only provided for those browsers that do not support it natively. [scope] becomes the %this% object.
|
1090
|
-
* @example
|
1091
|
-
*
|
1092
|
-
* ['a','b','c'].forEach(function(a) {
|
1093
|
-
* // Called 3 times: 'a','b','c'
|
1094
|
-
* });
|
1095
|
-
*
|
1096
|
-
***/
|
1097
|
-
'forEach': function(fn, scope) {
|
1098
|
-
var length = this.length, index = 0;
|
1099
|
-
checkCallback(fn);
|
1100
|
-
while(index < length) {
|
1101
|
-
if(index in this) {
|
1102
|
-
fn.call(scope, this[index], index, this);
|
1103
|
-
}
|
1104
|
-
index++;
|
1105
|
-
}
|
1106
|
-
},
|
1107
|
-
|
1108
|
-
/***
|
1109
|
-
* @method reduce([fn], [init])
|
1110
|
-
* @returns Mixed
|
1111
|
-
* @short Reduces the array to a single result.
|
1112
|
-
* @extra By default this method calls [fn] n - 1 times, where n is the length of the array. On the first call it is passed the first and second elements in the array. The result of that callback will then be passed into the next iteration until it reaches the end, where the accumulated value will be returned as the final result. If [init] is passed, it will call [fn] one extra time in the beginning passing in [init] along with the first element. This method is only provided for those browsers that do not support it natively.
|
1113
|
-
* @example
|
1114
|
-
*
|
1115
|
-
+ [1,2,3,4].reduce(function(a, b) {
|
1116
|
-
* return a + b;
|
1117
|
-
* });
|
1118
|
-
+ [1,2,3,4].reduce(function(a, b) {
|
1119
|
-
* return a + b;
|
1120
|
-
* }, 100);
|
1121
|
-
*
|
1122
|
-
***/
|
1123
|
-
'reduce': function(fn, init) {
|
1124
|
-
return arrayReduce(this, fn, init);
|
1125
|
-
},
|
1126
|
-
|
1127
|
-
/***
|
1128
|
-
* @method reduceRight([fn], [init])
|
1129
|
-
* @returns Mixed
|
1130
|
-
* @short Reduces the array to a single result by stepping through it from the right.
|
1131
|
-
* @extra By default this method calls [fn] n - 1 times, where n is the length of the array. On the first call it is passed the last and second to last elements in the array. The result of that callback will then be passed into the next iteration until it reaches the beginning, where the accumulated value will be returned as the final result. If [init] is passed, it will call [fn] one extra time in the beginning passing in [init] along with the last element. This method is only provided for those browsers that do not support it natively.
|
1132
|
-
* @example
|
1133
|
-
*
|
1134
|
-
+ [1,2,3,4].reduceRight(function(a, b) {
|
1135
|
-
* return a - b;
|
1136
|
-
* });
|
1137
|
-
*
|
1138
|
-
***/
|
1139
|
-
'reduceRight': function(fn, init) {
|
1140
|
-
return arrayReduce(this, fn, init, true);
|
1141
|
-
},
|
1142
|
-
|
1143
|
-
/***
|
1144
|
-
* @method each(<fn>, [index] = 0, [loop] = false)
|
1145
|
-
* @returns Array
|
1146
|
-
* @short Runs <fn> against elements in the array. Enhanced version of %Array#forEach%.
|
1147
|
-
* @extra Parameters passed to <fn> are identical to %forEach%, ie. the first parameter is the current element, second parameter is the current index, and third parameter is the array itself. If <fn> returns %false% at any time it will break out of the loop. Once %each% finishes, it will return the array. If [index] is passed, <fn> will begin at that index and work its way to the end. If [loop] is true, it will then start over from the beginning of the array and continue until it reaches [index] - 1.
|
1148
|
-
* @example
|
1149
|
-
*
|
1150
|
-
* [1,2,3,4].each(function(n) {
|
1151
|
-
* // Called 4 times: 1, 2, 3, 4
|
1152
|
-
* });
|
1153
|
-
* [1,2,3,4].each(function(n) {
|
1154
|
-
* // Called 4 times: 3, 4, 1, 2
|
1155
|
-
* }, 2, true);
|
1156
|
-
*
|
1157
|
-
***/
|
1158
|
-
'each': function(fn, index, loop) {
|
1159
|
-
arrayEach(this, fn, index, loop, true);
|
1160
|
-
return this;
|
1161
|
-
},
|
1162
|
-
|
1163
|
-
/***
|
1164
|
-
* @method find(<f>, [index] = 0, [loop] = false)
|
1165
|
-
* @returns Mixed
|
1166
|
-
* @short Returns the first element that matches <f>.
|
1167
|
-
* @extra <f> will match a string, number, array, object, or alternately test against a function or regex. Starts at [index], and will continue once from index = 0 if [loop] is true.
|
1168
|
-
* @example
|
1169
|
-
*
|
1170
|
-
+ [{a:1,b:2},{a:1,b:3},{a:1,b:4}].find(function(n) {
|
1171
|
-
* return n['a'] == 1;
|
1172
|
-
* }); -> {a:1,b:3}
|
1173
|
-
* ['cuba','japan','canada'].find(/^c/, 2) -> 'canada'
|
1174
|
-
*
|
1175
|
-
***/
|
1176
|
-
'find': function(f, index, loop) {
|
1177
|
-
return arrayFind(this, f, index, loop);
|
1178
|
-
},
|
1179
|
-
|
1180
|
-
/***
|
1181
|
-
* @method findAll(<f>, [index] = 0, [loop] = false)
|
1182
|
-
* @returns Array
|
1183
|
-
* @short Returns all elements that match <f>.
|
1184
|
-
* @extra <f> will match a string, number, array, object, or alternately test against a function or regex. Starts at [index], and will continue once from index = 0 if [loop] is true.
|
1185
|
-
* @example
|
1186
|
-
*
|
1187
|
-
+ [{a:1,b:2},{a:1,b:3},{a:2,b:4}].findAll(function(n) {
|
1188
|
-
* return n['a'] == 1;
|
1189
|
-
* }); -> [{a:1,b:3},{a:1,b:4}]
|
1190
|
-
* ['cuba','japan','canada'].findAll(/^c/) -> 'cuba','canada'
|
1191
|
-
* ['cuba','japan','canada'].findAll(/^c/, 2) -> 'canada'
|
1192
|
-
*
|
1193
|
-
***/
|
1194
|
-
'findAll': function(f, index, loop) {
|
1195
|
-
var result = [];
|
1196
|
-
arrayEach(this, function(el, i, arr) {
|
1197
|
-
if(multiMatch(el, f, arr, [i, arr])) {
|
1198
|
-
result.push(el);
|
1199
|
-
}
|
1200
|
-
}, index, loop);
|
1201
|
-
return result;
|
1202
|
-
},
|
1203
|
-
|
1204
|
-
/***
|
1205
|
-
* @method findIndex(<f>, [startIndex] = 0, [loop] = false)
|
1206
|
-
* @returns Number
|
1207
|
-
* @short Returns the index of the first element that matches <f> or -1 if not found.
|
1208
|
-
* @extra This method has a few notable differences to native %indexOf%. Although <f> will similarly match a primitive such as a string or number, it will also match deep objects and arrays that are not equal by reference (%===%). Additionally, if a function is passed it will be run as a matching function (similar to the behavior of %Array#filter%) rather than attempting to find that function itself by reference in the array. Finally, a regexp will be matched against elements in the array, presumed to be strings. Starts at [index], and will continue once from index = 0 if [loop] is true.
|
1209
|
-
* @example
|
1210
|
-
*
|
1211
|
-
+ [1,2,3,4].findIndex(3); -> 2
|
1212
|
-
+ [1,2,3,4].findIndex(function(n) {
|
1213
|
-
* return n % 2 == 0;
|
1214
|
-
* }); -> 1
|
1215
|
-
+ ['one','two','three'].findIndex(/th/); -> 2
|
1216
|
-
*
|
1217
|
-
***/
|
1218
|
-
'findIndex': function(f, startIndex, loop) {
|
1219
|
-
var index = arrayFind(this, f, startIndex, loop, true);
|
1220
|
-
return isUndefined(index) ? -1 : index;
|
1221
|
-
},
|
1222
|
-
|
1223
|
-
/***
|
1224
|
-
* @method count(<f>)
|
1225
|
-
* @returns Number
|
1226
|
-
* @short Counts all elements in the array that match <f>.
|
1227
|
-
* @extra <f> will match a string, number, array, object, or alternately test against a function or regex.
|
1228
|
-
* @example
|
1229
|
-
*
|
1230
|
-
* [1,2,3,1].count(1) -> 2
|
1231
|
-
* ['a','b','c'].count(/b/) -> 1
|
1232
|
-
+ [{a:1},{b:2}].count(function(n) {
|
1233
|
-
* return n['a'] > 1;
|
1234
|
-
* }); -> 0
|
1235
|
-
*
|
1236
|
-
***/
|
1237
|
-
'count': function(f) {
|
1238
|
-
if(isUndefined(f)) return this.length;
|
1239
|
-
return this.findAll(f).length;
|
1240
|
-
},
|
1241
|
-
|
1242
|
-
/***
|
1243
|
-
* @method none(<f>)
|
1244
|
-
* @returns Boolean
|
1245
|
-
* @short Returns true if none of the elements in the array match <f>.
|
1246
|
-
* @extra <f> will match a string, number, array, object, or alternately test against a function or regex.
|
1247
|
-
* @example
|
1248
|
-
*
|
1249
|
-
* [1,2,3].none(5) -> true
|
1250
|
-
* ['a','b','c'].none(/b/) -> false
|
1251
|
-
+ [{a:1},{b:2}].none(function(n) {
|
1252
|
-
* return n['a'] > 1;
|
1253
|
-
* }); -> true
|
1254
|
-
*
|
1255
|
-
***/
|
1256
|
-
'none': function() {
|
1257
|
-
return !this.any.apply(this, arguments);
|
1258
|
-
},
|
1259
|
-
|
1260
|
-
/***
|
1261
|
-
* @method remove([f1], [f2], ...)
|
1262
|
-
* @returns Array
|
1263
|
-
* @short Removes any element in the array that matches [f1], [f2], etc.
|
1264
|
-
* @extra Will match a string, number, array, object, or alternately test against a function or regex. This method will change the array! Use %exclude% for a non-destructive alias.
|
1265
|
-
* @example
|
1266
|
-
*
|
1267
|
-
* [1,2,3].remove(3) -> [1,2]
|
1268
|
-
* ['a','b','c'].remove(/b/) -> ['a','c']
|
1269
|
-
+ [{a:1},{b:2}].remove(function(n) {
|
1270
|
-
* return n['a'] == 1;
|
1271
|
-
* }); -> [{b:2}]
|
1272
|
-
*
|
1273
|
-
***/
|
1274
|
-
'remove': function() {
|
1275
|
-
var i, arr = this;
|
1276
|
-
multiArgs(arguments, function(f) {
|
1277
|
-
i = 0;
|
1278
|
-
while(i < arr.length) {
|
1279
|
-
if(multiMatch(arr[i], f, arr, [i, arr])) {
|
1280
|
-
arr.splice(i, 1);
|
1281
|
-
} else {
|
1282
|
-
i++;
|
1283
|
-
}
|
1284
|
-
}
|
1285
|
-
});
|
1286
|
-
return arr;
|
1287
|
-
},
|
1288
|
-
|
1289
|
-
/***
|
1290
|
-
* @method removeAt(<start>, [end])
|
1291
|
-
* @returns Array
|
1292
|
-
* @short Removes element at <start>. If [end] is specified, removes the range between <start> and [end]. This method will change the array! If you don't intend the array to be changed use %clone% first.
|
1293
|
-
* @example
|
1294
|
-
*
|
1295
|
-
* ['a','b','c'].removeAt(0) -> ['b','c']
|
1296
|
-
* [1,2,3,4].removeAt(1, 3) -> [1]
|
1297
|
-
*
|
1298
|
-
***/
|
1299
|
-
'removeAt': function(start, end) {
|
1300
|
-
if(isUndefined(start)) return this;
|
1301
|
-
if(isUndefined(end)) end = start;
|
1302
|
-
for(var i = 0; i <= (end - start); i++) {
|
1303
|
-
this.splice(start, 1);
|
1304
|
-
}
|
1305
|
-
return this;
|
1306
|
-
},
|
1307
|
-
|
1308
|
-
/***
|
1309
|
-
* @method add(<el>, [index])
|
1310
|
-
* @returns Array
|
1311
|
-
* @short Adds <el> to the array.
|
1312
|
-
* @extra If [index] is specified, it will add at [index], otherwise adds to the end of the array. %add% behaves like %concat% in that if <el> is an array it will be joined, not inserted. This method will change the array! Use %include% for a non-destructive alias. Also, %insert% is provided as an alias that reads better when using an index.
|
1313
|
-
* @example
|
1314
|
-
*
|
1315
|
-
* [1,2,3,4].add(5) -> [1,2,3,4,5]
|
1316
|
-
* [1,2,3,4].add([5,6,7]) -> [1,2,3,4,5,6,7]
|
1317
|
-
* [1,2,3,4].insert(8, 1) -> [1,8,2,3,4]
|
1318
|
-
*
|
1319
|
-
***/
|
1320
|
-
'add': function(el, index) {
|
1321
|
-
if(!object.isNumber(number(index)) || isNaN(index) || index == -1) index = this.length;
|
1322
|
-
else if(index < -1) index += 1;
|
1323
|
-
array.prototype.splice.apply(this, [index, 0].concat(el));
|
1324
|
-
return this;
|
1325
|
-
},
|
1326
|
-
|
1327
|
-
/***
|
1328
|
-
* @method include(<el>, [index])
|
1329
|
-
* @returns Array
|
1330
|
-
* @short Adds <el> to the array.
|
1331
|
-
* @extra This is a non-destructive alias for %add%. It will not change the original array.
|
1332
|
-
* @example
|
1333
|
-
*
|
1334
|
-
* [1,2,3,4].include(5) -> [1,2,3,4,5]
|
1335
|
-
* [1,2,3,4].include(8, 1) -> [1,8,2,3,4]
|
1336
|
-
* [1,2,3,4].include([5,6,7]) -> [1,2,3,4,5,6,7]
|
1337
|
-
*
|
1338
|
-
***/
|
1339
|
-
'include': function(el, index) {
|
1340
|
-
return this.clone().add(el, index);
|
1341
|
-
},
|
1342
|
-
|
1343
|
-
/***
|
1344
|
-
* @method exclude([f1], [f2], ...)
|
1345
|
-
* @returns Array
|
1346
|
-
* @short Removes any element in the array that matches [f1], [f2], etc.
|
1347
|
-
* @extra This is a non-destructive alias for %remove%. It will not change the original array.
|
1348
|
-
* @example
|
1349
|
-
*
|
1350
|
-
* [1,2,3].exclude(3) -> [1,2]
|
1351
|
-
* ['a','b','c'].exclude(/b/) -> ['a','c']
|
1352
|
-
+ [{a:1},{b:2}].exclude(function(n) {
|
1353
|
-
* return n['a'] == 1;
|
1354
|
-
* }); -> [{b:2}]
|
1355
|
-
*
|
1356
|
-
***/
|
1357
|
-
'exclude': function() {
|
1358
|
-
return array.prototype.remove.apply(this.clone(), arguments);
|
1359
|
-
},
|
1360
|
-
|
1361
|
-
/***
|
1362
|
-
* @method clone()
|
1363
|
-
* @returns Array
|
1364
|
-
* @short Clones the array.
|
1365
|
-
* @example
|
1366
|
-
*
|
1367
|
-
* [1,2,3].clone() -> [1,2,3]
|
1368
|
-
*
|
1369
|
-
***/
|
1370
|
-
'clone': function() {
|
1371
|
-
return object.merge([], this);
|
1372
|
-
},
|
1373
|
-
|
1374
|
-
/***
|
1375
|
-
* @method unique([map] = null)
|
1376
|
-
* @returns Array
|
1377
|
-
* @short Removes all duplicate elements in the array.
|
1378
|
-
* @extra [map] may be a function mapping the value to be uniqued on or a string acting as a shortcut. This is most commonly used when you have a key that ensures the object's uniqueness, and don't need to check all fields.
|
1379
|
-
* @example
|
1380
|
-
*
|
1381
|
-
* [1,2,2,3].unique() -> [1,2,3]
|
1382
|
-
* [{foo:'bar'},{foo:'bar'}].unique() -> [{foo:'bar'}]
|
1383
|
-
+ [{foo:'bar'},{foo:'bar'}].unique(function(obj){
|
1384
|
-
* return obj.foo;
|
1385
|
-
* }); -> [{foo:'bar'}]
|
1386
|
-
* [{foo:'bar'},{foo:'bar'}].unique('foo') -> [{foo:'bar'}]
|
1387
|
-
*
|
1388
|
-
***/
|
1389
|
-
'unique': function(map) {
|
1390
|
-
return arrayUnique(this, map);
|
1391
|
-
},
|
1392
|
-
|
1393
|
-
/***
|
1394
|
-
* @method union([a1], [a2], ...)
|
1395
|
-
* @returns Array
|
1396
|
-
* @short Returns an array containing all elements in all arrays with duplicates removed.
|
1397
|
-
* @example
|
1398
|
-
*
|
1399
|
-
* [1,3,5].union([5,7,9]) -> [1,3,5,7,9]
|
1400
|
-
* ['a','b'].union(['b','c']) -> ['a','b','c']
|
1401
|
-
*
|
1402
|
-
***/
|
1403
|
-
'union': function() {
|
1404
|
-
var arr = this;
|
1405
|
-
multiArgs(arguments, function(arg) {
|
1406
|
-
arr = arr.concat(arg);
|
1407
|
-
});
|
1408
|
-
return arrayUnique(arr);
|
1409
|
-
},
|
1410
|
-
|
1411
|
-
/***
|
1412
|
-
* @method intersect([a1], [a2], ...)
|
1413
|
-
* @returns Array
|
1414
|
-
* @short Returns an array containing the elements all arrays have in common.
|
1415
|
-
* @example
|
1416
|
-
*
|
1417
|
-
* [1,3,5].intersect([5,7,9]) -> [5]
|
1418
|
-
* ['a','b'].intersect('b','c') -> ['b']
|
1419
|
-
*
|
1420
|
-
***/
|
1421
|
-
'intersect': function() {
|
1422
|
-
return arrayIntersect(this, multiArgs(arguments, null, true), false);
|
1423
|
-
},
|
1424
|
-
|
1425
|
-
/***
|
1426
|
-
* @method subtract([a1], [a2], ...)
|
1427
|
-
* @returns Array
|
1428
|
-
* @short Subtracts from the array all elements in [a1], [a2], etc.
|
1429
|
-
* @example
|
1430
|
-
*
|
1431
|
-
* [1,3,5].subtract([5,7,9]) -> [1,3]
|
1432
|
-
* [1,3,5].subtract([3],[5]) -> [1]
|
1433
|
-
* ['a','b'].subtract('b','c') -> ['a']
|
1434
|
-
*
|
1435
|
-
***/
|
1436
|
-
'subtract': function(a) {
|
1437
|
-
return arrayIntersect(this, multiArgs(arguments, null, true), true);
|
1438
|
-
},
|
1439
|
-
|
1440
|
-
/***
|
1441
|
-
* @method at(<index>, [loop] = true)
|
1442
|
-
* @returns Mixed
|
1443
|
-
* @short Gets the element(s) at a given index.
|
1444
|
-
* @extra When [loop] is true, overshooting the end of the array (or the beginning) will begin counting from the other end. As an alternate syntax, passing multiple indexes will get the elements at those indexes.
|
1445
|
-
* @example
|
1446
|
-
*
|
1447
|
-
* [1,2,3].at(0) -> 1
|
1448
|
-
* [1,2,3].at(2) -> 3
|
1449
|
-
* [1,2,3].at(4) -> 2
|
1450
|
-
* [1,2,3].at(4, false) -> null
|
1451
|
-
* [1,2,3].at(-1) -> 3
|
1452
|
-
* [1,2,3].at(0,1) -> [1,2]
|
1453
|
-
*
|
1454
|
-
***/
|
1455
|
-
'at': function() {
|
1456
|
-
return entryAtIndex(this, arguments);
|
1457
|
-
},
|
1458
|
-
|
1459
|
-
/***
|
1460
|
-
* @method first([num] = 1)
|
1461
|
-
* @returns Mixed
|
1462
|
-
* @short Returns the first element(s) in the array.
|
1463
|
-
* @extra When <num> is passed, returns the first <num> elements in the array.
|
1464
|
-
* @example
|
1465
|
-
*
|
1466
|
-
* [1,2,3].first() -> 1
|
1467
|
-
* [1,2,3].first(2) -> [1,2]
|
1468
|
-
*
|
1469
|
-
***/
|
1470
|
-
'first': function(num) {
|
1471
|
-
if(isUndefined(num)) return this[0];
|
1472
|
-
if(num < 0) num = 0;
|
1473
|
-
return this.slice(0, num);
|
1474
|
-
},
|
1475
|
-
|
1476
|
-
/***
|
1477
|
-
* @method last([num] = 1)
|
1478
|
-
* @returns Mixed
|
1479
|
-
* @short Returns the last element(s) in the array.
|
1480
|
-
* @extra When <num> is passed, returns the last <num> elements in the array.
|
1481
|
-
* @example
|
1482
|
-
*
|
1483
|
-
* [1,2,3].last() -> 3
|
1484
|
-
* [1,2,3].last(2) -> [2,3]
|
1485
|
-
*
|
1486
|
-
***/
|
1487
|
-
'last': function(num) {
|
1488
|
-
if(isUndefined(num)) return this[this.length - 1];
|
1489
|
-
var start = this.length - num < 0 ? 0 : this.length - num;
|
1490
|
-
return this.slice(start);
|
1491
|
-
},
|
1492
|
-
|
1493
|
-
/***
|
1494
|
-
* @method from(<index>)
|
1495
|
-
* @returns Array
|
1496
|
-
* @short Returns a slice of the array from <index>.
|
1497
|
-
* @example
|
1498
|
-
*
|
1499
|
-
* [1,2,3].from(1) -> [2,3]
|
1500
|
-
* [1,2,3].from(2) -> [3]
|
1501
|
-
*
|
1502
|
-
***/
|
1503
|
-
'from': function(num) {
|
1504
|
-
return this.slice(num);
|
1505
|
-
},
|
1506
|
-
|
1507
|
-
/***
|
1508
|
-
* @method to(<index>)
|
1509
|
-
* @returns Array
|
1510
|
-
* @short Returns a slice of the array up to <index>.
|
1511
|
-
* @example
|
1512
|
-
*
|
1513
|
-
* [1,2,3].to(1) -> [1]
|
1514
|
-
* [1,2,3].to(2) -> [1,2]
|
1515
|
-
*
|
1516
|
-
***/
|
1517
|
-
'to': function(num) {
|
1518
|
-
if(isUndefined(num)) num = this.length;
|
1519
|
-
return this.slice(0, num);
|
1520
|
-
},
|
1521
|
-
|
1522
|
-
/***
|
1523
|
-
* @method min([map])
|
1524
|
-
* @returns Array
|
1525
|
-
* @short Returns the elements in the array with the lowest value.
|
1526
|
-
* @extra [map] may be a function mapping the value to be checked or a string acting as a shortcut.
|
1527
|
-
* @example
|
1528
|
-
*
|
1529
|
-
* [1,2,3].min() -> [1]
|
1530
|
-
* ['fee','fo','fum'].min('length') -> ['fo']
|
1531
|
-
+ ['fee','fo','fum'].min(function(n) {
|
1532
|
-
* return n.length;
|
1533
|
-
* }); -> ['fo']
|
1534
|
-
+ [{a:3,a:2}].min(function(n) {
|
1535
|
-
* return n['a'];
|
1536
|
-
* }); -> [{a:2}]
|
1537
|
-
*
|
1538
|
-
***/
|
1539
|
-
'min': function(map) {
|
1540
|
-
return arrayUnique(getMinOrMax(this, map, 'min', true));
|
1541
|
-
},
|
1542
|
-
|
1543
|
-
/***
|
1544
|
-
* @method max(<map>)
|
1545
|
-
* @returns Array
|
1546
|
-
* @short Returns the elements in the array with the greatest value.
|
1547
|
-
* @extra <map> may be a function mapping the value to be checked or a string acting as a shortcut.
|
1548
|
-
* @example
|
1549
|
-
*
|
1550
|
-
* [1,2,3].max() -> [3]
|
1551
|
-
* ['fee','fo','fum'].max('length') -> ['fee','fum']
|
1552
|
-
+ [{a:3,a:2}].max(function(n) {
|
1553
|
-
* return n['a'];
|
1554
|
-
* }); -> [{a:3}]
|
1555
|
-
*
|
1556
|
-
***/
|
1557
|
-
'max': function(map) {
|
1558
|
-
return arrayUnique(getMinOrMax(this, map, 'max', true));
|
1559
|
-
},
|
1560
|
-
|
1561
|
-
/***
|
1562
|
-
* @method least(<map>)
|
1563
|
-
* @returns Array
|
1564
|
-
* @short Returns the elements in the array with the least commonly occuring value.
|
1565
|
-
* @extra <map> may be a function mapping the value to be checked or a string acting as a shortcut.
|
1566
|
-
* @example
|
1567
|
-
*
|
1568
|
-
* [3,2,2].least() -> [3]
|
1569
|
-
* ['fe','fo','fum'].least('length') -> ['fum']
|
1570
|
-
+ [{age:35,name:'ken'},{age:12,name:'bob'},{age:12,name:'ted'}].least(function(n) {
|
1571
|
-
* return n.age;
|
1572
|
-
* }); -> [{age:35,name:'ken'}]
|
1573
|
-
*
|
1574
|
-
***/
|
1575
|
-
'least': function() {
|
1576
|
-
var result = arrayFlatten(getMinOrMax(this.groupBy.apply(this, arguments), 'length', 'min'));
|
1577
|
-
return result.length === this.length ? [] : arrayUnique(result);
|
1578
|
-
},
|
1579
|
-
|
1580
|
-
/***
|
1581
|
-
* @method most(<map>)
|
1582
|
-
* @returns Array
|
1583
|
-
* @short Returns the elements in the array with the most commonly occuring value.
|
1584
|
-
* @extra <map> may be a function mapping the value to be checked or a string acting as a shortcut.
|
1585
|
-
* @example
|
1586
|
-
*
|
1587
|
-
* [3,2,2].most() -> [2]
|
1588
|
-
* ['fe','fo','fum'].most('length') -> ['fe','fo']
|
1589
|
-
+ [{age:35,name:'ken'},{age:12,name:'bob'},{age:12,name:'ted'}].most(function(n) {
|
1590
|
-
* return n.age;
|
1591
|
-
* }); -> [{age:12,name:'bob'},{age:12,name:'ted'}]
|
1592
|
-
*
|
1593
|
-
***/
|
1594
|
-
'most': function() {
|
1595
|
-
var result = arrayFlatten(getMinOrMax(this.groupBy.apply(this, arguments), 'length', 'max'));
|
1596
|
-
return result.length === this.length ? [] : arrayUnique(result);
|
1597
|
-
},
|
1598
|
-
|
1599
|
-
/***
|
1600
|
-
* @method sum(<map>)
|
1601
|
-
* @returns Number
|
1602
|
-
* @short Sums all values in the array.
|
1603
|
-
* @extra <map> may be a function mapping the value to be summed or a string acting as a shortcut.
|
1604
|
-
* @example
|
1605
|
-
*
|
1606
|
-
* [1,2,2].sum() -> 5
|
1607
|
-
+ [{age:35},{age:12},{age:12}].sum(function(n) {
|
1608
|
-
* return n.age;
|
1609
|
-
* }); -> 59
|
1610
|
-
* [{age:35},{age:12},{age:12}].sum('age') -> 59
|
1611
|
-
*
|
1612
|
-
***/
|
1613
|
-
'sum': function(map) {
|
1614
|
-
var arr = map ? this.map(map) : this;
|
1615
|
-
return arr.length > 0 ? arr.reduce(function(a,b) { return a + b; }) : 0;
|
1616
|
-
},
|
1617
|
-
|
1618
|
-
/***
|
1619
|
-
* @method average(<map>)
|
1620
|
-
* @returns Number
|
1621
|
-
* @short Averages all values in the array.
|
1622
|
-
* @extra <map> may be a function mapping the value to be averaged or a string acting as a shortcut.
|
1623
|
-
* @example
|
1624
|
-
*
|
1625
|
-
* [1,2,3].average() -> 2
|
1626
|
-
+ [{age:35},{age:11},{age:11}].average(function(n) {
|
1627
|
-
* return n.age;
|
1628
|
-
* }); -> 19
|
1629
|
-
* [{age:35},{age:11},{age:11}].average('age') -> 19
|
1630
|
-
*
|
1631
|
-
***/
|
1632
|
-
'average': function(map) {
|
1633
|
-
var arr = map ? this.map(map) : this;
|
1634
|
-
return arr.length > 0 ? arr.sum() / arr.length : 0;
|
1635
|
-
},
|
1636
|
-
|
1637
|
-
/***
|
1638
|
-
* @method groupBy(<map>, [fn])
|
1639
|
-
* @returns Object
|
1640
|
-
* @short Groups the array by <map>.
|
1641
|
-
* @extra Will return an object with keys equal to the grouped values. <map> may be a mapping function, or a string acting as a shortcut. Optionally calls [fn] for each group.
|
1642
|
-
* @example
|
1643
|
-
*
|
1644
|
-
* ['fee','fi','fum'].groupBy('length') -> { 2: ['fi'], 3: ['fee','fum'] }
|
1645
|
-
+ [{age:35,name:'ken'},{age:15,name:'bob'}].groupBy(function(n) {
|
1646
|
-
* return n.age;
|
1647
|
-
* }); -> { 35: [{age:35,name:'ken'}], 15: [{age:15,name:'bob'}] }
|
1648
|
-
*
|
1649
|
-
***/
|
1650
|
-
'groupBy': function(map, fn) {
|
1651
|
-
var arr = this, result = object.extended(), key;
|
1652
|
-
arrayEach(arr, function(el, index) {
|
1653
|
-
key = transformArgument(el, map, arr, [el, index, arr]);
|
1654
|
-
if(!result[key]) result[key] = [];
|
1655
|
-
result[key].push(el);
|
1656
|
-
});
|
1657
|
-
return result.each(fn);
|
1658
|
-
},
|
1659
|
-
|
1660
|
-
/***
|
1661
|
-
* @method inGroups(<num>, [padding])
|
1662
|
-
* @returns Array
|
1663
|
-
* @short Groups the array into <num> arrays.
|
1664
|
-
* @extra [padding] specifies a value with which to pad the last array so that they are all equal length.
|
1665
|
-
* @example
|
1666
|
-
*
|
1667
|
-
* [1,2,3,4,5,6,7].inGroups(3) -> [ [1,2,3], [4,5,6], [7] ]
|
1668
|
-
* [1,2,3,4,5,6,7].inGroups(3, 'none') -> [ [1,2,3], [4,5,6], [7,'none','none'] ]
|
1669
|
-
*
|
1670
|
-
***/
|
1671
|
-
'inGroups': function(num, padding) {
|
1672
|
-
var pad = arguments.length > 1;
|
1673
|
-
var arr = this;
|
1674
|
-
var result = [];
|
1675
|
-
var divisor = (this.length / num).ceil();
|
1676
|
-
(0).upto(num - 1, function(i) {
|
1677
|
-
var index = i * divisor;
|
1678
|
-
var group = arr.slice(index, index + divisor);
|
1679
|
-
if(pad && group.length < divisor) {
|
1680
|
-
(divisor - group.length).times(function() {
|
1681
|
-
group = group.add(padding);
|
1682
|
-
});
|
1683
|
-
}
|
1684
|
-
result.push(group);
|
1685
|
-
});
|
1686
|
-
return result;
|
1687
|
-
},
|
1688
|
-
|
1689
|
-
/***
|
1690
|
-
* @method inGroupsOf(<num>, [padding] = null)
|
1691
|
-
* @returns Array
|
1692
|
-
* @short Groups the array into arrays of <num> elements each.
|
1693
|
-
* @extra [padding] specifies a value with which to pad the last array so that they are all equal length.
|
1694
|
-
* @example
|
1695
|
-
*
|
1696
|
-
* [1,2,3,4,5,6,7].inGroupsOf(4) -> [ [1,2,3,4], [5,6,7] ]
|
1697
|
-
* [1,2,3,4,5,6,7].inGroupsOf(4, 'none') -> [ [1,2,3,4], [5,6,7,'none'] ]
|
1698
|
-
*
|
1699
|
-
***/
|
1700
|
-
'inGroupsOf': function(num, padding) {
|
1701
|
-
if(this.length === 0 || num === 0) return this;
|
1702
|
-
if(isUndefined(num)) num = 1;
|
1703
|
-
if(isUndefined(padding)) padding = null;
|
1704
|
-
var result = [];
|
1705
|
-
var group = null;
|
1706
|
-
var len = this.length;
|
1707
|
-
this.each(function(el, i) {
|
1708
|
-
if((i % num) === 0) {
|
1709
|
-
if(group) result.push(group);
|
1710
|
-
group = [];
|
1711
|
-
}
|
1712
|
-
if(isUndefined(el)) el = padding;
|
1713
|
-
group.push(el);
|
1714
|
-
});
|
1715
|
-
if(!this.length.isMultipleOf(num)) {
|
1716
|
-
(num - (this.length % num)).times(function() {
|
1717
|
-
group.push(padding);
|
1718
|
-
});
|
1719
|
-
this.length = this.length + (num - (this.length % num));
|
1720
|
-
}
|
1721
|
-
if(group.length > 0) result.push(group);
|
1722
|
-
return result;
|
1723
|
-
},
|
1724
|
-
|
1725
|
-
/***
|
1726
|
-
* @method compact([all] = false)
|
1727
|
-
* @returns Array
|
1728
|
-
* @short Removes all instances of %undefined%, %null%, and %NaN% from the array.
|
1729
|
-
* @extra If [all] is %true%, all "falsy" elements will be removed. This includes empty strings, 0, and false.
|
1730
|
-
* @example
|
1731
|
-
*
|
1732
|
-
* [1,null,2,undefined,3].compact() -> [1,2,3]
|
1733
|
-
* [1,'',2,false,3].compact() -> [1,'',2,false,3]
|
1734
|
-
* [1,'',2,false,3].compact(true) -> [1,2,3]
|
1735
|
-
*
|
1736
|
-
***/
|
1737
|
-
'compact': function(all) {
|
1738
|
-
var result = [];
|
1739
|
-
arrayEach(this, function(el, i) {
|
1740
|
-
if(object.isArray(el)) {
|
1741
|
-
result.push(el.compact());
|
1742
|
-
} else if(all && el) {
|
1743
|
-
result.push(el);
|
1744
|
-
} else if(!all && el != null && !object.isNaN(el)) {
|
1745
|
-
result.push(el);
|
1746
|
-
}
|
1747
|
-
});
|
1748
|
-
return result;
|
1749
|
-
},
|
1750
|
-
|
1751
|
-
/***
|
1752
|
-
* @method isEmpty()
|
1753
|
-
* @returns Boolean
|
1754
|
-
* @short Returns true if the array is empty.
|
1755
|
-
* @extra This is true if the array has a length of zero, or contains only %undefined%, %null%, or %NaN%.
|
1756
|
-
* @example
|
1757
|
-
*
|
1758
|
-
* [].isEmpty() -> true
|
1759
|
-
* [null,undefined].isEmpty() -> true
|
1760
|
-
*
|
1761
|
-
***/
|
1762
|
-
'isEmpty': function() {
|
1763
|
-
return this.compact().length == 0;
|
1764
|
-
},
|
1765
|
-
|
1766
|
-
/***
|
1767
|
-
* @method flatten([limit] = Infinity)
|
1768
|
-
* @returns Array
|
1769
|
-
* @short Returns a flattened, one-dimensional copy of the array.
|
1770
|
-
* @extra You can optionally specify a [limit], which will only flatten that depth.
|
1771
|
-
* @example
|
1772
|
-
*
|
1773
|
-
* [[1], 2, [3]].flatten() -> [1,2,3]
|
1774
|
-
* [['a'],[],'b','c'].flatten() -> ['a','b','c']
|
1775
|
-
*
|
1776
|
-
***/
|
1777
|
-
'flatten': function(limit) {
|
1778
|
-
return arrayFlatten(this, limit);
|
1779
|
-
},
|
1780
|
-
|
1781
|
-
/***
|
1782
|
-
* @method sortBy(<map>, [desc] = false)
|
1783
|
-
* @returns Array
|
1784
|
-
* @short Sorts the array by <map>.
|
1785
|
-
* @extra <map> may be a function, a string acting as a shortcut, or blank (direct comparison of array values). [desc] will sort the array in descending order. When the field being sorted on is a string, the resulting order will be determined by an internal algorithm that is optimized for major Western languages, but can be customized. For more information see @array_sorting.
|
1786
|
-
* @example
|
1787
|
-
*
|
1788
|
-
* ['world','a','new'].sortBy('length') -> ['a','new','world']
|
1789
|
-
* ['world','a','new'].sortBy('length', true) -> ['world','new','a']
|
1790
|
-
+ [{age:72},{age:13},{age:18}].sortBy(function(n) {
|
1791
|
-
* return n.age;
|
1792
|
-
* }); -> [{age:13},{age:18},{age:72}]
|
1793
|
-
*
|
1794
|
-
***/
|
1795
|
-
'sortBy': function(map, desc) {
|
1796
|
-
var arr = this.clone();
|
1797
|
-
arr.sort(function(a, b) {
|
1798
|
-
var aProperty, bProperty, comp;
|
1799
|
-
aProperty = transformArgument(a, map, arr, [a]);
|
1800
|
-
bProperty = transformArgument(b, map, arr, [b]);
|
1801
|
-
if(object.isString(aProperty) && object.isString(bProperty)) {
|
1802
|
-
comp = collateStrings(aProperty, bProperty);
|
1803
|
-
} else if(aProperty < bProperty) {
|
1804
|
-
comp = -1;
|
1805
|
-
} else if(aProperty > bProperty) {
|
1806
|
-
comp = 1;
|
1807
|
-
} else {
|
1808
|
-
comp = 0;
|
1809
|
-
}
|
1810
|
-
return comp * (desc ? -1 : 1);
|
1811
|
-
});
|
1812
|
-
return arr;
|
1813
|
-
},
|
1814
|
-
|
1815
|
-
/***
|
1816
|
-
* @method randomize()
|
1817
|
-
* @returns Array
|
1818
|
-
* @short Randomizes the array.
|
1819
|
-
* @extra Uses Fisher-Yates algorithm.
|
1820
|
-
* @example
|
1821
|
-
*
|
1822
|
-
* [1,2,3,4].randomize() -> [?,?,?,?]
|
1823
|
-
*
|
1824
|
-
***/
|
1825
|
-
'randomize': function() {
|
1826
|
-
var a = this.concat();
|
1827
|
-
for(var j, x, i = a.length; i; j = parseInt(Math.random() * i), x = a[--i], a[i] = a[j], a[j] = x) {};
|
1828
|
-
return a;
|
1829
|
-
},
|
1830
|
-
|
1831
|
-
/***
|
1832
|
-
* @method zip([arr1], [arr2], ...)
|
1833
|
-
* @returns Array
|
1834
|
-
* @short Merges multiple arrays together.
|
1835
|
-
* @extra This method "zips up" smaller arrays into one large whose elements are "all elements at index 0", "all elements at index 1", etc. Useful when you have associated data that is split over separated arrays. If the arrays passed have more elements than the original array, they will be discarded. If they have fewer elements, the missing elements will filled with %null%.
|
1836
|
-
* @example
|
1837
|
-
*
|
1838
|
-
* [1,2,3].zip([4,5,6]) -> [[1,2], [3,4], [5,6]]
|
1839
|
-
* ['Martin','John'].zip(['Luther','F.'], ['King','Kennedy']) -> [['Martin','Luther','King'], ['John','F.','Kennedy']]
|
1840
|
-
*
|
1841
|
-
***/
|
1842
|
-
'zip': function() {
|
1843
|
-
var args = getArgs(arguments);
|
1844
|
-
return this.map(function(el, i) {
|
1845
|
-
return [el].concat(args.map(function(k) {
|
1846
|
-
return (i in k) ? k[i] : null;
|
1847
|
-
}));
|
1848
|
-
});
|
1849
|
-
},
|
1850
|
-
|
1851
|
-
/***
|
1852
|
-
* @method sample([num] = null)
|
1853
|
-
* @returns Mixed
|
1854
|
-
* @short Returns a random element from the array.
|
1855
|
-
* @extra If [num] is a number greater than 0, will return an array containing [num] samples.
|
1856
|
-
* @example
|
1857
|
-
*
|
1858
|
-
* [1,2,3,4,5].sample() -> // Random element
|
1859
|
-
* [1,2,3,4,5].sample(3) -> // Array of 3 random elements
|
1860
|
-
*
|
1861
|
-
***/
|
1862
|
-
'sample': function(num) {
|
1863
|
-
var result = [], arr = this.clone(), index;
|
1864
|
-
if(!(num > 0)) num = 1;
|
1865
|
-
while(result.length < num) {
|
1866
|
-
index = Number.random(0, arr.length - 1);
|
1867
|
-
result.push(arr[index]);
|
1868
|
-
arr.removeAt(index);
|
1869
|
-
if(arr.length == 0) break;
|
1870
|
-
}
|
1871
|
-
return arguments.length > 0 ? result : result[0];
|
1872
|
-
}
|
1873
|
-
|
1874
|
-
});
|
1875
|
-
|
1876
|
-
|
1877
|
-
// Aliases
|
1878
|
-
extend(array, true, false, {
|
1879
|
-
|
1880
|
-
/***
|
1881
|
-
* @method all()
|
1882
|
-
* @alias every
|
1883
|
-
*
|
1884
|
-
***/
|
1885
|
-
'all': array.prototype.every,
|
1886
|
-
|
1887
|
-
/*** @method any()
|
1888
|
-
* @alias some
|
1889
|
-
*
|
1890
|
-
***/
|
1891
|
-
'any': array.prototype.some,
|
1892
|
-
|
1893
|
-
/***
|
1894
|
-
* @method has()
|
1895
|
-
* @alias some
|
1896
|
-
*
|
1897
|
-
***/
|
1898
|
-
'has': array.prototype.some,
|
1899
|
-
|
1900
|
-
/***
|
1901
|
-
* @method insert()
|
1902
|
-
* @alias add
|
1903
|
-
*
|
1904
|
-
***/
|
1905
|
-
'insert': array.prototype.add
|
1906
|
-
|
1907
|
-
});
|
1908
|
-
|
1909
|
-
|
1910
|
-
|
1911
|
-
|
1912
|
-
|
1913
|
-
|
1914
|
-
|
1915
|
-
|
1916
|
-
|
1917
|
-
|
1918
|
-
/***
|
1919
|
-
* Number module
|
1920
|
-
*
|
1921
|
-
***/
|
1922
|
-
|
1923
|
-
|
1924
|
-
function round(val, precision, method) {
|
1925
|
-
var fn = Math[method || 'round'];
|
1926
|
-
var multiplier = Math.pow(10, (precision || 0).abs());
|
1927
|
-
if(precision < 0) multiplier = 1 / multiplier;
|
1928
|
-
return fn(val * multiplier) / multiplier;
|
1929
|
-
}
|
1930
|
-
|
1931
|
-
function getRange(start, stop, fn, step) {
|
1932
|
-
var arr = [], i = parseInt(start), up = step > 0;
|
1933
|
-
while((up && i <= stop) || (!up && i >= stop)) {
|
1934
|
-
arr.push(i);
|
1935
|
-
if(fn) fn.call(this, i);
|
1936
|
-
i += step;
|
1937
|
-
}
|
1938
|
-
return arr;
|
1939
|
-
}
|
1940
|
-
|
1941
|
-
function abbreviateNumber(num, roundTo, str, mid, limit, bytes) {
|
1942
|
-
var fixed = num.toFixed(20),
|
1943
|
-
decimalPlace = fixed.search(/\./),
|
1944
|
-
numeralPlace = fixed.search(/[1-9]/),
|
1945
|
-
significant = decimalPlace - numeralPlace,
|
1946
|
-
unit, i, divisor;
|
1947
|
-
if(significant > 0) {
|
1948
|
-
significant -= 1;
|
1949
|
-
}
|
1950
|
-
i = Math.max(Math.min((significant / 3).floor(), limit === false ? str.length : limit), -mid);
|
1951
|
-
unit = str.charAt(i + mid - 1);
|
1952
|
-
if(significant < -9) {
|
1953
|
-
i = -3;
|
1954
|
-
roundTo = significant.abs() - 9;
|
1955
|
-
unit = str.first();
|
1956
|
-
}
|
1957
|
-
divisor = bytes ? (2).pow(10 * i) : (10).pow(i * 3);
|
1958
|
-
return (num / divisor).round(roundTo || 0).format() + unit.trim();
|
1959
|
-
}
|
1960
|
-
|
1961
|
-
|
1962
|
-
extend(number, false, false, {
|
1963
|
-
|
1964
|
-
/***
|
1965
|
-
* @method Number.random([n1], [n2])
|
1966
|
-
* @returns Number
|
1967
|
-
* @short Returns a random integer between [n1] and [n2].
|
1968
|
-
* @extra If only 1 number is passed, the other will be 0. If none are passed, the number will be either 0 or 1.
|
1969
|
-
* @example
|
1970
|
-
*
|
1971
|
-
* Number.random(50, 100) -> ex. 85
|
1972
|
-
* Number.random(50) -> ex. 27
|
1973
|
-
* Number.random() -> ex. 0
|
1974
|
-
*
|
1975
|
-
***/
|
1976
|
-
'random': function(n1, n2) {
|
1977
|
-
var min, max;
|
1978
|
-
if(arguments.length == 1) n2 = n1, n1 = 0;
|
1979
|
-
min = Math.min(n1 || 0, isUndefined(n2) ? 1 : n2);
|
1980
|
-
max = Math.max(n1 || 0, isUndefined(n2) ? 1 : n2);
|
1981
|
-
return round((Math.random() * (max - min)) + min);
|
1982
|
-
}
|
1983
|
-
|
1984
|
-
});
|
1985
|
-
|
1986
|
-
extend(number, true, false, {
|
1987
|
-
|
1988
|
-
/***
|
1989
|
-
* @method toNumber()
|
1990
|
-
* @returns Number
|
1991
|
-
* @short Returns a number. This is mostly for compatibility reasons.
|
1992
|
-
* @example
|
1993
|
-
*
|
1994
|
-
* (420).toNumber() -> 420
|
1995
|
-
*
|
1996
|
-
***/
|
1997
|
-
'toNumber': function() {
|
1998
|
-
return parseFloat(this, 10);
|
1999
|
-
},
|
2000
|
-
|
2001
|
-
/***
|
2002
|
-
* @method abbr([precision] = 0)
|
2003
|
-
* @returns String
|
2004
|
-
* @short Returns an abbreviated form of the number.
|
2005
|
-
* @extra [precision] will round to the given precision.
|
2006
|
-
* @example
|
2007
|
-
*
|
2008
|
-
* (1000).abbr() -> "1k"
|
2009
|
-
* (1000000).abbr() -> "1m"
|
2010
|
-
* (1280).abbr(1) -> "1.3k"
|
2011
|
-
*
|
2012
|
-
***/
|
2013
|
-
'abbr': function(precision) {
|
2014
|
-
return abbreviateNumber(this, precision, 'kmbt', 0, 4);
|
2015
|
-
},
|
2016
|
-
|
2017
|
-
/***
|
2018
|
-
* @method metric([precision] = 0, [limit] = 1)
|
2019
|
-
* @returns String
|
2020
|
-
* @short Returns the number as a string in metric notation.
|
2021
|
-
* @extra [precision] will round to the given precision. Both very large numbers and very small numbers are supported. [limit] is the upper limit for the units. The default is %1%, which is "kilo". If [limit] is %false%, the upper limit will be "exa". The lower limit is "nano", and cannot be changed.
|
2022
|
-
* @example
|
2023
|
-
*
|
2024
|
-
* (1000).metric() -> "1k"
|
2025
|
-
* (1000000).metric() -> "1,000k"
|
2026
|
-
* (1000000).metric(0, false) -> "1M"
|
2027
|
-
* (1249).metric(2) + 'g' -> "1.25kg"
|
2028
|
-
* (0.025).metric() + 'm' -> "25mm"
|
2029
|
-
*
|
2030
|
-
***/
|
2031
|
-
'metric': function(precision, limit) {
|
2032
|
-
return abbreviateNumber(this, precision, 'nμm kMGTPE', 4, isUndefined(limit) ? 1 : limit);
|
2033
|
-
},
|
2034
|
-
|
2035
|
-
/***
|
2036
|
-
* @method bytes([precision] = 0, [limit] = 4)
|
2037
|
-
* @returns String
|
2038
|
-
* @short Returns an abbreviated form of the number, considered to be "Bytes".
|
2039
|
-
* @extra [precision] will round to the given precision. [limit] is the upper limit for the units. The default is %4%, which is "terabytes" (TB). If [limit] is %false%, the upper limit will be "exa".
|
2040
|
-
* @example
|
2041
|
-
*
|
2042
|
-
* (1000).bytes() -> "1kB"
|
2043
|
-
* (1000).bytes(2) -> "0.98kB"
|
2044
|
-
* ((10).pow(20)).bytes() -> "90,949,470TB"
|
2045
|
-
* ((10).pow(20)).bytes(0, false) -> "87EB"
|
2046
|
-
*
|
2047
|
-
***/
|
2048
|
-
'bytes': function(precision, limit) {
|
2049
|
-
return abbreviateNumber(this, precision, 'kMGTPE', 0, isUndefined(limit) ? 4 : limit, true) + 'B';
|
2050
|
-
},
|
2051
|
-
|
2052
|
-
/***
|
2053
|
-
* @method isInteger()
|
2054
|
-
* @returns Boolean
|
2055
|
-
* @short Returns true if the number has no trailing decimal.
|
2056
|
-
* @example
|
2057
|
-
*
|
2058
|
-
* (420).isInteger() -> true
|
2059
|
-
* (4.5).isInteger() -> false
|
2060
|
-
*
|
2061
|
-
***/
|
2062
|
-
'isInteger': function() {
|
2063
|
-
return this % 1 == 0;
|
2064
|
-
},
|
2065
|
-
|
2066
|
-
/***
|
2067
|
-
* @method ceil([precision] = 0)
|
2068
|
-
* @returns Number
|
2069
|
-
* @short Rounds the number up. [precision] will round to the given precision.
|
2070
|
-
* @example
|
2071
|
-
*
|
2072
|
-
* (4.434).ceil() -> 5
|
2073
|
-
* (-4.434).ceil() -> -4
|
2074
|
-
* (44.17).ceil(1) -> 44.2
|
2075
|
-
* (4417).ceil(-2) -> 4500
|
2076
|
-
*
|
2077
|
-
***/
|
2078
|
-
'ceil': function(precision) {
|
2079
|
-
return round(this, precision, 'ceil');
|
2080
|
-
},
|
2081
|
-
|
2082
|
-
/***
|
2083
|
-
* @method floor([precision] = 0)
|
2084
|
-
* @returns Number
|
2085
|
-
* @short Rounds the number down. [precision] will round to the given precision.
|
2086
|
-
* @example
|
2087
|
-
*
|
2088
|
-
* (4.434).floor() -> 4
|
2089
|
-
* (-4.434).floor() -> -5
|
2090
|
-
* (44.17).floor(1) -> 44.1
|
2091
|
-
* (4417).floor(-2) -> 4400
|
2092
|
-
*
|
2093
|
-
***/
|
2094
|
-
'floor': function(precision) {
|
2095
|
-
return round(this, precision, 'floor');
|
2096
|
-
},
|
2097
|
-
|
2098
|
-
/***
|
2099
|
-
* @method abs()
|
2100
|
-
* @returns Number
|
2101
|
-
* @short Returns the absolute value for the number.
|
2102
|
-
* @example
|
2103
|
-
*
|
2104
|
-
* (3).abs() -> 3
|
2105
|
-
* (-3).abs() -> 3
|
2106
|
-
*
|
2107
|
-
***/
|
2108
|
-
'abs': function() {
|
2109
|
-
return Math.abs(this);
|
2110
|
-
},
|
2111
|
-
|
2112
|
-
/***
|
2113
|
-
* @method pow(<p> = 1)
|
2114
|
-
* @returns Number
|
2115
|
-
* @short Returns the number to the power of <p>.
|
2116
|
-
* @example
|
2117
|
-
*
|
2118
|
-
* (3).pow(2) -> 9
|
2119
|
-
* (3).pow(3) -> 27
|
2120
|
-
* (3).pow() -> 3
|
2121
|
-
*
|
2122
|
-
***/
|
2123
|
-
'pow': function(power) {
|
2124
|
-
if(isUndefined(power)) power = 1;
|
2125
|
-
return Math.pow(this, power);
|
2126
|
-
},
|
2127
|
-
|
2128
|
-
/***
|
2129
|
-
* @method round(<precision> = 0)
|
2130
|
-
* @returns Number
|
2131
|
-
* @short Rounds a number to the precision of <precision>.
|
2132
|
-
* @example
|
2133
|
-
*
|
2134
|
-
* (3.241).round() -> 3
|
2135
|
-
* (3.841).round() -> 4
|
2136
|
-
* (-3.241).round() -> -3
|
2137
|
-
* (-3.841).round() -> -4
|
2138
|
-
* (3.241).round(2) -> 3.24
|
2139
|
-
* (3748).round(-2) -> 3800
|
2140
|
-
*
|
2141
|
-
***/
|
2142
|
-
'round': function(precision) {
|
2143
|
-
return round(this, precision, 'round');
|
2144
|
-
},
|
2145
|
-
|
2146
|
-
/***
|
2147
|
-
* @method chr()
|
2148
|
-
* @returns String
|
2149
|
-
* @short Returns a string at the code point of the number.
|
2150
|
-
* @example
|
2151
|
-
*
|
2152
|
-
* (65).chr() -> "A"
|
2153
|
-
* (75).chr() -> "K"
|
2154
|
-
*
|
2155
|
-
***/
|
2156
|
-
'chr': function() {
|
2157
|
-
return string.fromCharCode(this);
|
2158
|
-
},
|
2159
|
-
|
2160
|
-
/***
|
2161
|
-
* @method isOdd()
|
2162
|
-
* @returns Boolean
|
2163
|
-
* @short Returns true if the number is odd.
|
2164
|
-
* @example
|
2165
|
-
*
|
2166
|
-
* (3).isOdd() -> true
|
2167
|
-
* (18).isOdd() -> false
|
2168
|
-
*
|
2169
|
-
***/
|
2170
|
-
'isOdd': function() {
|
2171
|
-
return !this.isMultipleOf(2);
|
2172
|
-
},
|
2173
|
-
|
2174
|
-
/***
|
2175
|
-
* @method isEven()
|
2176
|
-
* @returns Boolean
|
2177
|
-
* @short Returns true if the number is even.
|
2178
|
-
* @example
|
2179
|
-
*
|
2180
|
-
* (6).isEven() -> true
|
2181
|
-
* (17).isEven() -> false
|
2182
|
-
*
|
2183
|
-
***/
|
2184
|
-
'isEven': function() {
|
2185
|
-
return this.isMultipleOf(2);
|
2186
|
-
},
|
2187
|
-
|
2188
|
-
/***
|
2189
|
-
* @method isMultipleOf(<num>)
|
2190
|
-
* @returns Boolean
|
2191
|
-
* @short Returns true if the number is a multiple of <num>.
|
2192
|
-
* @example
|
2193
|
-
*
|
2194
|
-
* (6).isMultipleOf(2) -> true
|
2195
|
-
* (17).isMultipleOf(2) -> false
|
2196
|
-
* (32).isMultipleOf(4) -> true
|
2197
|
-
* (34).isMultipleOf(4) -> false
|
2198
|
-
*
|
2199
|
-
***/
|
2200
|
-
'isMultipleOf': function(num) {
|
2201
|
-
return this % num === 0;
|
2202
|
-
},
|
2203
|
-
|
2204
|
-
/***
|
2205
|
-
* @method upto(<num>, [fn], [step] = 1)
|
2206
|
-
* @returns Array
|
2207
|
-
* @short Returns an array containing numbers from the number up to <num>.
|
2208
|
-
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
|
2209
|
-
* @example
|
2210
|
-
*
|
2211
|
-
* (2).upto(6) -> [2, 3, 4, 5, 6]
|
2212
|
-
* (2).upto(6, function(n) {
|
2213
|
-
* // This function is called 5 times receiving n as the value.
|
2214
|
-
* });
|
2215
|
-
* (2).upto(8, null, 2) -> [2, 4, 6, 8]
|
2216
|
-
*
|
2217
|
-
***/
|
2218
|
-
'upto': function(num, fn, step) {
|
2219
|
-
return getRange(this, num, fn, step || 1);
|
2220
|
-
},
|
2221
|
-
|
2222
|
-
/***
|
2223
|
-
* @method downto(<num>, [fn], [step] = 1)
|
2224
|
-
* @returns Array
|
2225
|
-
* @short Returns an array containing numbers from the number down to <num>.
|
2226
|
-
* @extra Optionally calls [fn] callback for each number in that array. [step] allows multiples greater than 1.
|
2227
|
-
* @example
|
2228
|
-
*
|
2229
|
-
* (8).downto(3) -> [8, 7, 6, 5, 4, 3]
|
2230
|
-
* (8).downto(3, function(n) {
|
2231
|
-
* // This function is called 6 times receiving n as the value.
|
2232
|
-
* });
|
2233
|
-
* (8).downto(2, null, 2) -> [8, 6, 4, 2]
|
2234
|
-
*
|
2235
|
-
***/
|
2236
|
-
'downto': function(num, fn, step) {
|
2237
|
-
return getRange(this, num, fn, -(step || 1));
|
2238
|
-
},
|
2239
|
-
|
2240
|
-
|
2241
|
-
/***
|
2242
|
-
* @method times(<fn>)
|
2243
|
-
* @returns Number
|
2244
|
-
* @short Calls <fn> a number of times equivalent to the number.
|
2245
|
-
* @example
|
2246
|
-
*
|
2247
|
-
* (8).times(function(i) {
|
2248
|
-
* // This function is called 8 times.
|
2249
|
-
* });
|
2250
|
-
*
|
2251
|
-
***/
|
2252
|
-
'times': function(fn) {
|
2253
|
-
if(fn) {
|
2254
|
-
for(var i = 0; i < this; i++) {
|
2255
|
-
fn.call(this, i);
|
2256
|
-
}
|
2257
|
-
}
|
2258
|
-
return this.toNumber();
|
2259
|
-
},
|
2260
|
-
|
2261
|
-
/***
|
2262
|
-
* @method ordinalize()
|
2263
|
-
* @returns String
|
2264
|
-
* @short Returns an ordinalized (English) string, i.e. "1st", "2nd", etc.
|
2265
|
-
* @example
|
2266
|
-
*
|
2267
|
-
* (1).ordinalize() -> '1st';
|
2268
|
-
* (2).ordinalize() -> '2nd';
|
2269
|
-
* (8).ordinalize() -> '8th';
|
2270
|
-
*
|
2271
|
-
***/
|
2272
|
-
'ordinalize': function() {
|
2273
|
-
var suffix, num = this.abs(), last = num.toString().last(2).toNumber();
|
2274
|
-
if(last >= 11 && last <= 13) {
|
2275
|
-
suffix = 'th';
|
2276
|
-
} else {
|
2277
|
-
switch(num % 10) {
|
2278
|
-
case 1: suffix = 'st'; break;
|
2279
|
-
case 2: suffix = 'nd'; break;
|
2280
|
-
case 3: suffix = 'rd'; break;
|
2281
|
-
default: suffix = 'th';
|
2282
|
-
}
|
2283
|
-
}
|
2284
|
-
return this.toString() + suffix;
|
2285
|
-
},
|
2286
|
-
|
2287
|
-
|
2288
|
-
/***
|
2289
|
-
* @method pad(<place> = 0, [sign] = false, [base] = 10)
|
2290
|
-
* @returns String
|
2291
|
-
* @short Pads a number with "0" to <place>.
|
2292
|
-
* @extra [sign] allows you to force the sign as well (+05, etc). [base] can change the base for numeral conversion.
|
2293
|
-
* @example
|
2294
|
-
*
|
2295
|
-
* (5).pad(2) -> '05'
|
2296
|
-
* (-5).pad(4) -> '-0005'
|
2297
|
-
* (82).pad(3, true) -> '+082'
|
2298
|
-
*
|
2299
|
-
***/
|
2300
|
-
'pad': function(place, sign, base) {
|
2301
|
-
base = base || 10;
|
2302
|
-
var str = this.toNumber() === 0 ? '' : this.toString(base).replace(/^-/, '');
|
2303
|
-
str = padString(str, '0', place - str.replace(/\.\d+$/, '').length, 0);
|
2304
|
-
if(sign || this < 0) {
|
2305
|
-
str = (this < 0 ? '-' : '+') + str;
|
2306
|
-
}
|
2307
|
-
return str;
|
2308
|
-
},
|
2309
|
-
|
2310
|
-
/***
|
2311
|
-
* @method format([place] = 0, [thousands] = ',', [decimal] = '.')
|
2312
|
-
* @returns String
|
2313
|
-
* @short Formats the number to a readable string.
|
2314
|
-
* @extra If [place] is %undefined%, will automatically determine the place. [thousands] is the character used for the thousands separator. [decimal] is the character used for the decimal point.
|
2315
|
-
* @example
|
2316
|
-
*
|
2317
|
-
* (56782).format() -> '56,782'
|
2318
|
-
* (56782).format(2) -> '56,782.00'
|
2319
|
-
* (4388.43).format(2, ' ') -> '4 388.43'
|
2320
|
-
* (4388.43).format(2, '.', ',') -> '4.388,43'
|
2321
|
-
*
|
2322
|
-
***/
|
2323
|
-
'format': function(place, thousands, decimal) {
|
2324
|
-
var str, split, method, after, r = /(\d+)(\d{3})/;
|
2325
|
-
if(string(thousands).match(/\d/)) throw new TypeError('Thousands separator cannot contain numbers.');
|
2326
|
-
str = object.isNumber(place) ? round(this, place).toFixed(Math.max(place, 0)) : this.toString();
|
2327
|
-
thousands = thousands || ',';
|
2328
|
-
decimal = decimal || '.';
|
2329
|
-
split = str.split('.');
|
2330
|
-
str = split[0];
|
2331
|
-
after = split[1] || '';
|
2332
|
-
while (str.match(r)) {
|
2333
|
-
str = str.replace(r, '$1' + thousands + '$2');
|
2334
|
-
}
|
2335
|
-
if(after.length > 0) {
|
2336
|
-
str += decimal + padString(after, '0', 0, place - after.length);
|
2337
|
-
}
|
2338
|
-
return str;
|
2339
|
-
},
|
2340
|
-
|
2341
|
-
/***
|
2342
|
-
* @method hex([pad] = 1)
|
2343
|
-
* @returns String
|
2344
|
-
* @short Converts the number to hexidecimal.
|
2345
|
-
* @extra [pad] will pad the resulting string to that many places.
|
2346
|
-
* @example
|
2347
|
-
*
|
2348
|
-
* (255).hex() -> 'ff';
|
2349
|
-
* (255).hex(4) -> '00ff';
|
2350
|
-
* (23654).hex() -> '5c66';
|
2351
|
-
*
|
2352
|
-
***/
|
2353
|
-
'hex': function(pad) {
|
2354
|
-
return this.pad(pad || 1, false, 16);
|
2355
|
-
}
|
2356
|
-
|
2357
|
-
});
|
2358
|
-
|
2359
|
-
|
2360
|
-
|
2361
|
-
|
2362
|
-
|
2363
|
-
/***
|
2364
|
-
* String module
|
2365
|
-
*
|
2366
|
-
***/
|
2367
|
-
|
2368
|
-
|
2369
|
-
// WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
|
2370
|
-
var getTrimmableCharacters = function() {
|
2371
|
-
return '\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u2028\u2029\u3000\uFEFF';
|
2372
|
-
}
|
2373
|
-
|
2374
|
-
/***
|
2375
|
-
* @method has[Script]()
|
2376
|
-
* @returns Boolean
|
2377
|
-
* @short Returns true if the string contains any characters in that script.
|
2378
|
-
* @example
|
2379
|
-
*
|
2380
|
-
* 'أتكلم'.hasArabic() -> true
|
2381
|
-
* 'визит'.hasCyrillic() -> true
|
2382
|
-
* '잘 먹겠습니다!'.hasHangul() -> true
|
2383
|
-
* 'ミックスです'.hasKatakana() -> true
|
2384
|
-
* "l'année".hasLatin() -> true
|
2385
|
-
*
|
2386
|
-
***
|
2387
|
-
* @method is[Script]()
|
2388
|
-
* @returns Boolean
|
2389
|
-
* @short Returns true if the string contains only characters in that script. Whitespace is ignored.
|
2390
|
-
* @example
|
2391
|
-
*
|
2392
|
-
* 'أتكلم'.isArabic() -> true
|
2393
|
-
* 'визит'.isCyrillic() -> true
|
2394
|
-
* '잘 먹겠습니다!'.isHangul() -> true
|
2395
|
-
* 'ミックスです'.isKatakana() -> false
|
2396
|
-
* "l'année".isLatin() -> true
|
2397
|
-
*
|
2398
|
-
***
|
2399
|
-
* @method hasArabic()
|
2400
|
-
* @set hasScript
|
2401
|
-
***
|
2402
|
-
* @method isArabic()
|
2403
|
-
* @set isScript
|
2404
|
-
****
|
2405
|
-
* @method hasCyrillic()
|
2406
|
-
* @set hasScript
|
2407
|
-
***
|
2408
|
-
* @method isCyrillic()
|
2409
|
-
* @set isScript
|
2410
|
-
****
|
2411
|
-
* @method hasGreek()
|
2412
|
-
* @set hasScript
|
2413
|
-
***
|
2414
|
-
* @method isGreek()
|
2415
|
-
* @set isScript
|
2416
|
-
****
|
2417
|
-
* @method hasHangul()
|
2418
|
-
* @set hasScript
|
2419
|
-
***
|
2420
|
-
* @method isHangul()
|
2421
|
-
* @set isScript
|
2422
|
-
****
|
2423
|
-
* @method hasHan()
|
2424
|
-
* @set hasScript
|
2425
|
-
***
|
2426
|
-
* @method isHan()
|
2427
|
-
* @set isScript
|
2428
|
-
****
|
2429
|
-
* @method hasKanji()
|
2430
|
-
* @set hasScript
|
2431
|
-
***
|
2432
|
-
* @method isKanji()
|
2433
|
-
* @set isScript
|
2434
|
-
****
|
2435
|
-
* @method hasHebrew()
|
2436
|
-
* @set hasScript
|
2437
|
-
***
|
2438
|
-
* @method isHebrew()
|
2439
|
-
* @set isScript
|
2440
|
-
****
|
2441
|
-
* @method hasHiragana()
|
2442
|
-
* @set hasScript
|
2443
|
-
***
|
2444
|
-
* @method isHiragana()
|
2445
|
-
* @set isScript
|
2446
|
-
****
|
2447
|
-
* @method hasKana()
|
2448
|
-
* @set hasScript
|
2449
|
-
***
|
2450
|
-
* @method isKana()
|
2451
|
-
* @set isScript
|
2452
|
-
****
|
2453
|
-
* @method hasKatakana()
|
2454
|
-
* @set hasScript
|
2455
|
-
***
|
2456
|
-
* @method isKatakana()
|
2457
|
-
* @set isScript
|
2458
|
-
****
|
2459
|
-
* @method hasLatin()
|
2460
|
-
* @set hasScript
|
2461
|
-
***
|
2462
|
-
* @method isKatakana()
|
2463
|
-
* @set isScript
|
2464
|
-
****
|
2465
|
-
* @method hasThai()
|
2466
|
-
* @set hasScript
|
2467
|
-
***
|
2468
|
-
* @method isThai()
|
2469
|
-
* @set isScript
|
2470
|
-
****
|
2471
|
-
* @method hasDevanagari()
|
2472
|
-
* @set hasScript
|
2473
|
-
***
|
2474
|
-
* @method isDevanagari()
|
2475
|
-
* @set isScript
|
2476
|
-
***/
|
2477
|
-
var unicodeScripts = [
|
2478
|
-
{ names: ['Arabic'], source: '\u0600-\u06FF' },
|
2479
|
-
{ names: ['Cyrillic'], source: '\u0400-\u04FF' },
|
2480
|
-
{ names: ['Devanagari'], source: '\u0900-\u097F' },
|
2481
|
-
{ names: ['Greek'], source: '\u0370-\u03FF' },
|
2482
|
-
{ names: ['Hangul'], source: '\uAC00-\uD7AF\u1100-\u11FF' },
|
2483
|
-
{ names: ['Han','Kanji'], source: '\u4E00-\u9FFF\uF900-\uFAFF' },
|
2484
|
-
{ names: ['Hebrew'], source: '\u0590-\u05FF' },
|
2485
|
-
{ names: ['Hiragana'], source: '\u3040-\u309F\u30FB-\u30FC' },
|
2486
|
-
{ names: ['Kana'], source: '\u3040-\u30FF\uFF61-\uFF9F' },
|
2487
|
-
{ names: ['Katakana'], source: '\u30A0-\u30FF\uFF61-\uFF9F' },
|
2488
|
-
{ names: ['Latin'], source: '\u0001-\u007F\u0080-\u00FF\u0100-\u017F\u0180-\u024F' },
|
2489
|
-
{ names: ['Thai'], source: '\u0E00-\u0E7F' }
|
2490
|
-
];
|
2491
|
-
|
2492
|
-
function buildUnicodeScripts() {
|
2493
|
-
unicodeScripts.each(function(s) {
|
2494
|
-
var is = regexp('^['+s.source+'\\s]+$');
|
2495
|
-
var has = regexp('['+s.source+']');
|
2496
|
-
s.names.each(function(name) {
|
2497
|
-
defineProperty(string.prototype, 'is' + name, function() { return is.test(this.trim()); });
|
2498
|
-
defineProperty(string.prototype, 'has' + name, function() { return has.test(this); });
|
2499
|
-
});
|
2500
|
-
});
|
2501
|
-
}
|
2502
|
-
|
2503
|
-
function convertCharacterWidth(str, args, reg, table) {
|
2504
|
-
var mode = getArgs(args).join('');
|
2505
|
-
mode = mode.replace(/all/, '').replace(/(\w)lphabet|umbers?|atakana|paces?|unctuation/g, '$1');
|
2506
|
-
return str.replace(reg, function(c) {
|
2507
|
-
if(table[c] && (!mode || mode.has(table[c].type))) {
|
2508
|
-
return table[c].to;
|
2509
|
-
} else {
|
2510
|
-
return c;
|
2511
|
-
}
|
2512
|
-
});
|
2513
|
-
}
|
2514
|
-
|
2515
|
-
var widthConversionRanges = [
|
2516
|
-
{ type: 'a', shift: 65248, start: 65, end: 90 },
|
2517
|
-
{ type: 'a', shift: 65248, start: 97, end: 122 },
|
2518
|
-
{ type: 'n', shift: 65248, start: 48, end: 57 },
|
2519
|
-
{ type: 'p', shift: 65248, start: 33, end: 47 },
|
2520
|
-
{ type: 'p', shift: 65248, start: 58, end: 64 },
|
2521
|
-
{ type: 'p', shift: 65248, start: 91, end: 96 },
|
2522
|
-
{ type: 'p', shift: 65248, start: 123, end: 126 }
|
2523
|
-
];
|
2524
|
-
|
2525
|
-
var ZenkakuTable = {};
|
2526
|
-
var HankakuTable = {};
|
2527
|
-
var allHankaku = /[\u0020-\u00A5]|[\uFF61-\uFF9F][゙゚]?/g;
|
2528
|
-
var allZenkaku = /[\u3000-\u301C]|[\u301A-\u30FC]|[\uFF01-\uFF60]|[\uFFE0-\uFFE6]/g;
|
2529
|
-
var hankakuPunctuation = '。、「」¥¢£';
|
2530
|
-
var zenkakuPunctuation = '。、「」¥¢£';
|
2531
|
-
var voicedKatakana = /[カキクケコサシスセソタチツテトハヒフヘホ]/;
|
2532
|
-
var semiVoicedKatakana = /[ハヒフヘホヲ]/;
|
2533
|
-
var hankakuKatakana = 'アイウエオァィゥェォカキクケコサシスセソタチツッテトナニヌネノハヒフヘホマミムメモヤャユュヨョラリルレロワヲンー・';
|
2534
|
-
var zenkakuKatakana = 'アイウエオァィゥェォカキクケコサシスセソタチツッテトナニヌネノハヒフヘホマミムメモヤャユュヨョラリルレロワヲンー・';
|
2535
|
-
|
2536
|
-
|
2537
|
-
function buildWidthConversionTables() {
|
2538
|
-
var hankaku;
|
2539
|
-
arrayEach(widthConversionRanges, function(r) {
|
2540
|
-
r.start.upto(r.end, function(n) {
|
2541
|
-
setWidthConversion(r.type, n.chr(), (n + r.shift).chr());
|
2542
|
-
});
|
2543
|
-
});
|
2544
|
-
zenkakuKatakana.each(function(c, i) {
|
2545
|
-
hankaku = hankakuKatakana.charAt(i);
|
2546
|
-
setWidthConversion('k', hankaku, c);
|
2547
|
-
if(c.match(voicedKatakana)) {
|
2548
|
-
setWidthConversion('k', hankaku + '゙', c.shift(1));
|
2549
|
-
}
|
2550
|
-
if(c.match(semiVoicedKatakana)) {
|
2551
|
-
setWidthConversion('k', hankaku + '゚', c.shift(2));
|
2552
|
-
}
|
2553
|
-
});
|
2554
|
-
zenkakuPunctuation.each(function(c, i) {
|
2555
|
-
setWidthConversion('p', hankakuPunctuation.charAt(i), c);
|
2556
|
-
});
|
2557
|
-
setWidthConversion('k', 'ヴ', 'ヴ');
|
2558
|
-
setWidthConversion('k', 'ヺ', 'ヺ');
|
2559
|
-
setWidthConversion('s', ' ', ' ');
|
2560
|
-
}
|
2561
|
-
|
2562
|
-
function setWidthConversion(type, half, full) {
|
2563
|
-
ZenkakuTable[half] = { type: type, to: full };
|
2564
|
-
HankakuTable[full] = { type: type, to: half };
|
2565
|
-
}
|
2566
|
-
|
2567
|
-
function padString(str, p, left, right) {
|
2568
|
-
var padding = String(p);
|
2569
|
-
if(padding != p) {
|
2570
|
-
padding = '';
|
2571
|
-
}
|
2572
|
-
if(!object.isNumber(left)) left = 1;
|
2573
|
-
if(!object.isNumber(right)) right = 1;
|
2574
|
-
return padding.repeat(left) + str + padding.repeat(right);
|
2575
|
-
}
|
2576
|
-
|
2577
|
-
function getAcronym(word) {
|
2578
|
-
return string.Inflector && string.Inflector.acronyms && string.Inflector.acronyms[word];
|
2579
|
-
}
|
2580
|
-
|
2581
|
-
var btoa, atob;
|
2582
|
-
|
2583
|
-
function buildBase64(key) {
|
2584
|
-
if(this.btoa) {
|
2585
|
-
btoa = this.btoa;
|
2586
|
-
atob = this.atob;
|
2587
|
-
}
|
2588
|
-
var base64reg = /[^A-Za-z0-9\+\/\=]/g;
|
2589
|
-
btoa = function(str) {
|
2590
|
-
var output = '';
|
2591
|
-
var chr1, chr2, chr3;
|
2592
|
-
var enc1, enc2, enc3, enc4;
|
2593
|
-
var i = 0;
|
2594
|
-
do {
|
2595
|
-
chr1 = str.charCodeAt(i++);
|
2596
|
-
chr2 = str.charCodeAt(i++);
|
2597
|
-
chr3 = str.charCodeAt(i++);
|
2598
|
-
enc1 = chr1 >> 2;
|
2599
|
-
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
|
2600
|
-
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
|
2601
|
-
enc4 = chr3 & 63;
|
2602
|
-
if (isNaN(chr2)) {
|
2603
|
-
enc3 = enc4 = 64;
|
2604
|
-
} else if (isNaN(chr3)) {
|
2605
|
-
enc4 = 64;
|
2606
|
-
}
|
2607
|
-
output = output + key.charAt(enc1) + key.charAt(enc2) + key.charAt(enc3) + key.charAt(enc4);
|
2608
|
-
chr1 = chr2 = chr3 = '';
|
2609
|
-
enc1 = enc2 = enc3 = enc4 = '';
|
2610
|
-
} while (i < str.length);
|
2611
|
-
return output;
|
2612
|
-
}
|
2613
|
-
atob = function(input) {
|
2614
|
-
var output = '';
|
2615
|
-
var chr1, chr2, chr3;
|
2616
|
-
var enc1, enc2, enc3, enc4;
|
2617
|
-
var i = 0;
|
2618
|
-
if(input.match(base64reg)) {
|
2619
|
-
throw new Error('String contains invalid base64 characters');
|
2620
|
-
}
|
2621
|
-
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, '');
|
2622
|
-
do {
|
2623
|
-
enc1 = key.indexOf(input.charAt(i++));
|
2624
|
-
enc2 = key.indexOf(input.charAt(i++));
|
2625
|
-
enc3 = key.indexOf(input.charAt(i++));
|
2626
|
-
enc4 = key.indexOf(input.charAt(i++));
|
2627
|
-
chr1 = (enc1 << 2) | (enc2 >> 4);
|
2628
|
-
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
|
2629
|
-
chr3 = ((enc3 & 3) << 6) | enc4;
|
2630
|
-
output = output + chr1.chr();
|
2631
|
-
if (enc3 != 64) {
|
2632
|
-
output = output + chr2.chr();
|
2633
|
-
}
|
2634
|
-
if (enc4 != 64) {
|
2635
|
-
output = output + chr3.chr();
|
2636
|
-
}
|
2637
|
-
chr1 = chr2 = chr3 = '';
|
2638
|
-
enc1 = enc2 = enc3 = enc4 = '';
|
2639
|
-
} while (i < input.length);
|
2640
|
-
return unescape(output);
|
2641
|
-
}
|
2642
|
-
}
|
2643
|
-
|
2644
|
-
function buildTrim() {
|
2645
|
-
var support = getTrimmableCharacters().match(/^\s+$/);
|
2646
|
-
try { string.prototype.trim.call([1]); } catch(e) { support = false; }
|
2647
|
-
var trimL = regexp('^['+getTrimmableCharacters()+']+');
|
2648
|
-
var trimR = regexp('['+getTrimmableCharacters()+']+$');
|
2649
|
-
extend(string, true, !support, {
|
2650
|
-
|
2651
|
-
/***
|
2652
|
-
* @method trim[Side]()
|
2653
|
-
* @returns String
|
2654
|
-
* @short Removes leading and/or trailing whitespace from the string.
|
2655
|
-
* @extra Whitespace is defined as line breaks, tabs, and any character in the "Space, Separator" Unicode category, conforming to the the ES5 spec. The standard %trim% method is only added when not fully supported natively.
|
2656
|
-
* @example
|
2657
|
-
*
|
2658
|
-
* ' wasabi '.trim() -> 'wasabi'
|
2659
|
-
* ' wasabi '.trimLeft() -> 'wasabi '
|
2660
|
-
* ' wasabi '.trimRight() -> ' wasabi'
|
2661
|
-
*
|
2662
|
-
***
|
2663
|
-
* @method trim()
|
2664
|
-
* @set trimSide
|
2665
|
-
***/
|
2666
|
-
'trim': function() {
|
2667
|
-
return this.toString().trimLeft().trimRight();
|
2668
|
-
},
|
2669
|
-
|
2670
|
-
/***
|
2671
|
-
* @method trimLeft()
|
2672
|
-
* @set trimSide
|
2673
|
-
***/
|
2674
|
-
'trimLeft': function() {
|
2675
|
-
return this.replace(trimL, '');
|
2676
|
-
},
|
2677
|
-
|
2678
|
-
/***
|
2679
|
-
* @method trimRight()
|
2680
|
-
* @set trimSide
|
2681
|
-
***/
|
2682
|
-
'trimRight': function() {
|
2683
|
-
return this.replace(trimR, '');
|
2684
|
-
}
|
2685
|
-
});
|
2686
|
-
}
|
2687
|
-
|
2688
|
-
function buildString() {
|
2689
|
-
buildBase64('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=');
|
2690
|
-
buildTrim();
|
2691
|
-
buildWidthConversionTables();
|
2692
|
-
buildUnicodeScripts();
|
2693
|
-
}
|
2694
|
-
|
2695
|
-
|
2696
|
-
|
2697
|
-
extend(string, true, false, {
|
2698
|
-
|
2699
|
-
/***
|
2700
|
-
* @method escapeRegExp()
|
2701
|
-
* @returns String
|
2702
|
-
* @short Escapes all RegExp tokens in the string.
|
2703
|
-
* @example
|
2704
|
-
*
|
2705
|
-
* 'really?'.escapeRegExp() -> 'really\?'
|
2706
|
-
* 'yes.'.escapeRegExp() -> 'yes\.'
|
2707
|
-
* '(not really)'.escapeRegExp() -> '\(not really\)'
|
2708
|
-
*
|
2709
|
-
***/
|
2710
|
-
'escapeRegExp': function() {
|
2711
|
-
return regexp.escape(this);
|
2712
|
-
},
|
2713
|
-
|
2714
|
-
/***
|
2715
|
-
* @method escapeURL([param] = false)
|
2716
|
-
* @returns String
|
2717
|
-
* @short Escapes characters in a string to make a valid URL.
|
2718
|
-
* @extra If [param] is true, it will also escape valid URL characters for use as a URL parameter.
|
2719
|
-
* @example
|
2720
|
-
*
|
2721
|
-
* 'http://foo.com/"bar"'.escapeURL() -> 'http://foo.com/%22bar%22'
|
2722
|
-
* 'http://foo.com/"bar"'.escapeURL(true) -> 'http%3A%2F%2Ffoo.com%2F%22bar%22'
|
2723
|
-
*
|
2724
|
-
***/
|
2725
|
-
'escapeURL': function(param) {
|
2726
|
-
return param ? encodeURIComponent(this) : encodeURI(this);
|
2727
|
-
},
|
2728
|
-
|
2729
|
-
/***
|
2730
|
-
* @method unescapeURL([partial] = false)
|
2731
|
-
* @returns String
|
2732
|
-
* @short Restores escaped characters in a URL escaped string.
|
2733
|
-
* @extra If [partial] is true, it will only unescape non-valid URL characters. [partial] is included here for completeness, but should very rarely be needed.
|
2734
|
-
* @example
|
2735
|
-
*
|
2736
|
-
* 'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL() -> 'http://foo.com/the bar'
|
2737
|
-
* 'http%3A%2F%2Ffoo.com%2Fthe%20bar'.unescapeURL(true) -> 'http%3A%2F%2Ffoo.com%2Fthe bar'
|
2738
|
-
*
|
2739
|
-
***/
|
2740
|
-
'unescapeURL': function(param) {
|
2741
|
-
return param ? decodeURI(this) : decodeURIComponent(this);
|
2742
|
-
},
|
2743
|
-
|
2744
|
-
/***
|
2745
|
-
* @method escapeHTML()
|
2746
|
-
* @returns String
|
2747
|
-
* @short Converts HTML characters to their entity equivalents.
|
2748
|
-
* @example
|
2749
|
-
*
|
2750
|
-
* '<p>some text</p>'.escapeHTML() -> '<p>some text</p>'
|
2751
|
-
* 'one & two'.escapeHTML() -> 'one & two'
|
2752
|
-
*
|
2753
|
-
***/
|
2754
|
-
'escapeHTML': function() {
|
2755
|
-
return this.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
2756
|
-
},
|
2757
|
-
|
2758
|
-
/***
|
2759
|
-
* @method unescapeHTML([partial] = false)
|
2760
|
-
* @returns String
|
2761
|
-
* @short Restores escaped HTML characters.
|
2762
|
-
* @example
|
2763
|
-
*
|
2764
|
-
* '<p>some text</p>'.unescapeHTML() -> '<p>some text</p>'
|
2765
|
-
* 'one & two'.unescapeHTML() -> 'one & two'
|
2766
|
-
*
|
2767
|
-
***/
|
2768
|
-
'unescapeHTML': function() {
|
2769
|
-
return this.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
2770
|
-
},
|
2771
|
-
|
2772
|
-
/***
|
2773
|
-
* @method encodeBase64()
|
2774
|
-
* @returns String
|
2775
|
-
* @short Encodes the string into base 64 encoding.
|
2776
|
-
* @extra This methods wraps the browser native %btoa% when available, and uses a custom implementation when not available.
|
2777
|
-
* @example
|
2778
|
-
*
|
2779
|
-
* 'gonna get encoded!'.encodeBase64() -> 'Z29ubmEgZ2V0IGVuY29kZWQh'
|
2780
|
-
* 'http://twitter.com/'.encodeBase64() -> 'aHR0cDovL3R3aXR0ZXIuY29tLw=='
|
2781
|
-
*
|
2782
|
-
***/
|
2783
|
-
'encodeBase64': function() {
|
2784
|
-
return btoa(this);
|
2785
|
-
},
|
2786
|
-
|
2787
|
-
/***
|
2788
|
-
* @method decodeBase64()
|
2789
|
-
* @returns String
|
2790
|
-
* @short Decodes the string from base 64 encoding.
|
2791
|
-
* @extra This methods wraps the browser native %atob% when available, and uses a custom implementation when not available.
|
2792
|
-
* @example
|
2793
|
-
*
|
2794
|
-
* 'aHR0cDovL3R3aXR0ZXIuY29tLw=='.decodeBase64() -> 'http://twitter.com/'
|
2795
|
-
* 'anVzdCBnb3QgZGVjb2RlZA=='.decodeBase64() -> 'just got decoded!'
|
2796
|
-
*
|
2797
|
-
***/
|
2798
|
-
'decodeBase64': function() {
|
2799
|
-
return atob(this);
|
2800
|
-
},
|
2801
|
-
|
2802
|
-
/***
|
2803
|
-
* @method capitalize([all] = false)
|
2804
|
-
* @returns String
|
2805
|
-
* @short Capitalizes the first character in the string.
|
2806
|
-
* @extra If [all] is true, all words in the string will be capitalized.
|
2807
|
-
* @example
|
2808
|
-
*
|
2809
|
-
* 'hello'.capitalize() -> 'hello'
|
2810
|
-
* 'hello kitty'.capitalize() -> 'hello kitty'
|
2811
|
-
* 'hello kitty'.capitalize(true) -> 'hello kitty'
|
2812
|
-
*
|
2813
|
-
*
|
2814
|
-
***/
|
2815
|
-
'capitalize': function(all) {
|
2816
|
-
var reg = all ? /^\S|\s\S/g : /^\S/;
|
2817
|
-
return this.toLowerCase().replace(reg, function(letter) {
|
2818
|
-
return letter.toUpperCase();
|
2819
|
-
});
|
2820
|
-
},
|
2821
|
-
|
2822
|
-
/***
|
2823
|
-
* @method pad[Side](<padding> = '', [num] = 1)
|
2824
|
-
* @returns String
|
2825
|
-
* @short Pads either/both sides of the string.
|
2826
|
-
* @extra [num] is the number of characters on each side, and [padding] is the character to pad with.
|
2827
|
-
* @example
|
2828
|
-
*
|
2829
|
-
* 'wasabi'.pad('-') -> '-wasabi-'
|
2830
|
-
* 'wasabi'.pad('-', 2) -> '--wasabi--'
|
2831
|
-
* 'wasabi'.padLeft('-', 2) -> '--wasabi'
|
2832
|
-
* 'wasabi'.padRight('-', 2) -> 'wasabi--'
|
2833
|
-
*
|
2834
|
-
***
|
2835
|
-
* @method pad()
|
2836
|
-
* @set padSide
|
2837
|
-
***/
|
2838
|
-
'pad': function(padding, num) {
|
2839
|
-
return padString(this, padding, num, num);
|
2840
|
-
},
|
2841
|
-
|
2842
|
-
/***
|
2843
|
-
* @method padLeft()
|
2844
|
-
* @set padSide
|
2845
|
-
***/
|
2846
|
-
'padLeft': function(padding, num) {
|
2847
|
-
return padString(this, padding, num, 0);
|
2848
|
-
},
|
2849
|
-
|
2850
|
-
/***
|
2851
|
-
* @method padRight()
|
2852
|
-
* @set padSide
|
2853
|
-
***/
|
2854
|
-
'padRight': function(padding, num) {
|
2855
|
-
return padString(this, padding, 0, num);
|
2856
|
-
},
|
2857
|
-
|
2858
|
-
/***
|
2859
|
-
* @method repeat([num] = 0)
|
2860
|
-
* @returns String
|
2861
|
-
* @short Returns the string repeated [num] times.
|
2862
|
-
* @example
|
2863
|
-
*
|
2864
|
-
* 'jumpy'.repeat(2) -> 'jumpyjumpy'
|
2865
|
-
* 'a'.repeat(5) -> 'aaaaa'
|
2866
|
-
*
|
2867
|
-
***/
|
2868
|
-
'repeat': function(num) {
|
2869
|
-
var str = '', i = 0;
|
2870
|
-
if(object.isNumber(num) && num > 0) {
|
2871
|
-
while(i < num) {
|
2872
|
-
str += this;
|
2873
|
-
i++;
|
2874
|
-
}
|
2875
|
-
}
|
2876
|
-
return str;
|
2877
|
-
},
|
2878
|
-
|
2879
|
-
/***
|
2880
|
-
* @method each([search] = single character, [fn])
|
2881
|
-
* @returns Array
|
2882
|
-
* @short Runs callback [fn] against each occurence of [search].
|
2883
|
-
* @extra Returns an array of matches. [search] may be either a string or regex, and defaults to every character in the string.
|
2884
|
-
* @example
|
2885
|
-
*
|
2886
|
-
* 'jumpy'.each() -> ['j','u','m','p','y']
|
2887
|
-
* 'jumpy'.each(/[r-z]/) -> ['u','y']
|
2888
|
-
* 'jumpy'.each(/[r-z]/, function(m) {
|
2889
|
-
* // Called twice: "u", "y"
|
2890
|
-
* });
|
2891
|
-
*
|
2892
|
-
***/
|
2893
|
-
'each': function(search, fn) {
|
2894
|
-
if(object.isFunction(search)) {
|
2895
|
-
fn = search;
|
2896
|
-
search = /[\s\S]/g;
|
2897
|
-
} else if(!search) {
|
2898
|
-
search = /[\s\S]/g
|
2899
|
-
} else if(object.isString(search)) {
|
2900
|
-
search = regexp(regexp.escape(search), 'gi');
|
2901
|
-
} else if(object.isRegExp(search)) {
|
2902
|
-
search = search.addFlag('g');
|
2903
|
-
}
|
2904
|
-
var match = this.match(search) || [];
|
2905
|
-
if(fn) {
|
2906
|
-
for(var i = 0; i < match.length; i++) {
|
2907
|
-
match[i] = fn.call(this, match[i], i, match) || match[i];
|
2908
|
-
}
|
2909
|
-
}
|
2910
|
-
return match;
|
2911
|
-
},
|
2912
|
-
|
2913
|
-
/***
|
2914
|
-
* @method shift(<n>)
|
2915
|
-
* @returns Array
|
2916
|
-
* @short Shifts each character in the string <n> places in the character map.
|
2917
|
-
* @example
|
2918
|
-
*
|
2919
|
-
* 'a'.shift(1) -> 'b'
|
2920
|
-
* 'ク'.shift(1) -> 'グ'
|
2921
|
-
*
|
2922
|
-
***/
|
2923
|
-
'shift': function(n) {
|
2924
|
-
var result = '';
|
2925
|
-
n = n || 0;
|
2926
|
-
this.codes(function(c) {
|
2927
|
-
result += (c + n).chr();
|
2928
|
-
});
|
2929
|
-
return result;
|
2930
|
-
},
|
2931
|
-
|
2932
|
-
/***
|
2933
|
-
* @method codes([fn])
|
2934
|
-
* @returns Array
|
2935
|
-
* @short Runs callback [fn] against each character code in the string. Returns an array of character codes.
|
2936
|
-
* @example
|
2937
|
-
*
|
2938
|
-
* 'jumpy'.codes() -> [106,117,109,112,121]
|
2939
|
-
* 'jumpy'.codes(function(c) {
|
2940
|
-
* // Called 5 times: 106, 117, 109, 112, 121
|
2941
|
-
* });
|
2942
|
-
*
|
2943
|
-
***/
|
2944
|
-
'codes': function(fn) {
|
2945
|
-
var codes = [];
|
2946
|
-
for(var i=0; i<this.length; i++) {
|
2947
|
-
var code = this.charCodeAt(i);
|
2948
|
-
codes.push(code);
|
2949
|
-
if(fn) fn.call(this, code, i);
|
2950
|
-
}
|
2951
|
-
return codes;
|
2952
|
-
},
|
2953
|
-
|
2954
|
-
/***
|
2955
|
-
* @method chars([fn])
|
2956
|
-
* @returns Array
|
2957
|
-
* @short Runs callback [fn] against each character in the string. Returns an array of characters.
|
2958
|
-
* @example
|
2959
|
-
*
|
2960
|
-
* 'jumpy'.chars() -> ['j','u','m','p','y']
|
2961
|
-
* 'jumpy'.chars(function(c) {
|
2962
|
-
* // Called 5 times: "j","u","m","p","y"
|
2963
|
-
* });
|
2964
|
-
*
|
2965
|
-
***/
|
2966
|
-
'chars': function(fn) {
|
2967
|
-
return this.each(fn);
|
2968
|
-
},
|
2969
|
-
|
2970
|
-
/***
|
2971
|
-
* @method words([fn])
|
2972
|
-
* @returns Array
|
2973
|
-
* @short Runs callback [fn] against each word in the string. Returns an array of words.
|
2974
|
-
* @extra A "word" here is defined as any sequence of non-whitespace characters.
|
2975
|
-
* @example
|
2976
|
-
*
|
2977
|
-
* 'broken wear'.words() -> ['broken','wear']
|
2978
|
-
* 'broken wear'.words(function(w) {
|
2979
|
-
* // Called twice: "broken", "wear"
|
2980
|
-
* });
|
2981
|
-
*
|
2982
|
-
***/
|
2983
|
-
'words': function(fn) {
|
2984
|
-
return this.trim().each(/\S+/g, fn);
|
2985
|
-
},
|
2986
|
-
|
2987
|
-
/***
|
2988
|
-
* @method lines([fn])
|
2989
|
-
* @returns Array
|
2990
|
-
* @short Runs callback [fn] against each line in the string. Returns an array of lines.
|
2991
|
-
* @example
|
2992
|
-
*
|
2993
|
-
* 'broken wear\nand\njumpy jump'.lines() -> ['broken wear','and','jumpy jump']
|
2994
|
-
* 'broken wear\nand\njumpy jump'.lines(function(l) {
|
2995
|
-
* // Called three times: "broken wear", "and", "jumpy jump"
|
2996
|
-
* });
|
2997
|
-
*
|
2998
|
-
***/
|
2999
|
-
'lines': function(fn) {
|
3000
|
-
return this.trim().each(/^.*$/gm, fn);
|
3001
|
-
},
|
3002
|
-
|
3003
|
-
/***
|
3004
|
-
* @method paragraphs([fn])
|
3005
|
-
* @returns Array
|
3006
|
-
* @short Runs callback [fn] against each paragraph in the string. Returns an array of paragraphs.
|
3007
|
-
* @extra A paragraph here is defined as a block of text bounded by two or more line breaks.
|
3008
|
-
* @example
|
3009
|
-
*
|
3010
|
-
* 'Once upon a time.\n\nIn the land of oz...'.paragraphs() -> ['Once upon a time.','In the land of oz...']
|
3011
|
-
* 'Once upon a time.\n\nIn the land of oz...'.paragraphs(function(p) {
|
3012
|
-
* // Called twice: "Once upon a time.", "In teh land of oz..."
|
3013
|
-
* });
|
3014
|
-
*
|
3015
|
-
***/
|
3016
|
-
'paragraphs': function(fn) {
|
3017
|
-
var paragraphs = this.trim().split(/[\r\n]{2,}/);
|
3018
|
-
paragraphs = paragraphs.map(function(p) {
|
3019
|
-
if(fn) var s = fn.call(p);
|
3020
|
-
return s ? s : p;
|
3021
|
-
});
|
3022
|
-
return paragraphs;
|
3023
|
-
},
|
3024
|
-
|
3025
|
-
/***
|
3026
|
-
* @method startsWith(<find>, [case] = true)
|
3027
|
-
* @returns Boolean
|
3028
|
-
* @short Returns true if the string starts with <find>.
|
3029
|
-
* @extra <find> may be either a string or regex. Case sensitive if [case] is true.
|
3030
|
-
* @example
|
3031
|
-
*
|
3032
|
-
* 'hello'.startsWith('hell') -> true
|
3033
|
-
* 'hello'.startsWith(/[a-h]/) -> true
|
3034
|
-
* 'hello'.startsWith('HELL') -> false
|
3035
|
-
* 'hello'.startsWith('HELL', false) -> true
|
3036
|
-
*
|
3037
|
-
***/
|
3038
|
-
'startsWith': function(reg, c) {
|
3039
|
-
if(isUndefined(c)) c = true;
|
3040
|
-
var source = object.isRegExp(reg) ? reg.source.replace('^', '') : regexp.escape(reg);
|
3041
|
-
return regexp('^' + source, c ? '' : 'i').test(this);
|
3042
|
-
},
|
3043
|
-
|
3044
|
-
/***
|
3045
|
-
* @method endsWith(<find>, [case] = true)
|
3046
|
-
* @returns Boolean
|
3047
|
-
* @short Returns true if the string ends with <find>.
|
3048
|
-
* @extra <find> may be either a string or regex. Case sensitive if [case] is true.
|
3049
|
-
* @example
|
3050
|
-
*
|
3051
|
-
* 'jumpy'.endsWith('py') -> true
|
3052
|
-
* 'jumpy'.endsWith(/[q-z]/) -> true
|
3053
|
-
* 'jumpy'.endsWith('MPY') -> false
|
3054
|
-
* 'jumpy'.endsWith('MPY', false) -> true
|
3055
|
-
*
|
3056
|
-
***/
|
3057
|
-
'endsWith': function(reg, c) {
|
3058
|
-
if(isUndefined(c)) c = true;
|
3059
|
-
var source = object.isRegExp(reg) ? reg.source.replace('$', '') : regexp.escape(reg);
|
3060
|
-
return regexp(source + '$', c ? '' : 'i').test(this);
|
3061
|
-
},
|
3062
|
-
|
3063
|
-
/***
|
3064
|
-
* @method isBlank()
|
3065
|
-
* @returns Boolean
|
3066
|
-
* @short Returns true if the string has a length of 0 or contains only whitespace.
|
3067
|
-
* @example
|
3068
|
-
*
|
3069
|
-
* ''.isBlank() -> true
|
3070
|
-
* ' '.isBlank() -> true
|
3071
|
-
* 'noway'.isBlank() -> false
|
3072
|
-
*
|
3073
|
-
***/
|
3074
|
-
'isBlank': function() {
|
3075
|
-
return this.trim().length === 0;
|
3076
|
-
},
|
3077
|
-
|
3078
|
-
/***
|
3079
|
-
* @method has(<find>)
|
3080
|
-
* @returns Boolean
|
3081
|
-
* @short Returns true if the string matches <find>.
|
3082
|
-
* @extra <find> may be a string or regex.
|
3083
|
-
* @example
|
3084
|
-
*
|
3085
|
-
* 'jumpy'.has('py') -> true
|
3086
|
-
* 'broken'.has(/[a-n]/) -> true
|
3087
|
-
* 'broken'.has(/[s-z]/) -> false
|
3088
|
-
*
|
3089
|
-
***/
|
3090
|
-
'has': function(find) {
|
3091
|
-
return this.search(object.isRegExp(find) ? find : RegExp.escape(find)) !== -1;
|
3092
|
-
},
|
3093
|
-
|
3094
|
-
|
3095
|
-
/***
|
3096
|
-
* @method add(<str>, [index] = 0)
|
3097
|
-
* @returns String
|
3098
|
-
* @short Adds <str> at [index]. Negative values are also allowed.
|
3099
|
-
* @extra %insert% is provided as an alias, and is generally more readable when using an index.
|
3100
|
-
* @example
|
3101
|
-
*
|
3102
|
-
* 'schfifty'.add(' five') -> schfifty five
|
3103
|
-
* 'dopamine'.insert('e', 3) -> dopeamine
|
3104
|
-
* 'spelling eror'.insert('r', -3) -> spelling error
|
3105
|
-
*
|
3106
|
-
***/
|
3107
|
-
'add': function(str, index) {
|
3108
|
-
return this.split('').add(str, index).join('');
|
3109
|
-
},
|
3110
|
-
|
3111
|
-
/***
|
3112
|
-
* @method remove(<f>)
|
3113
|
-
* @returns String
|
3114
|
-
* @short Removes any part of the string that matches <f>.
|
3115
|
-
* @extra <f> can be a string or a regex.
|
3116
|
-
* @example
|
3117
|
-
*
|
3118
|
-
* 'schfifty five'.remove('f') -> 'schity ive'
|
3119
|
-
* 'schfifty five'.remove(/[a-f]/g) -> 'shity iv'
|
3120
|
-
*
|
3121
|
-
***/
|
3122
|
-
'remove': function(f) {
|
3123
|
-
return this.replace(f, '');
|
3124
|
-
},
|
3125
|
-
|
3126
|
-
/***
|
3127
|
-
* @method hankaku([mode] = 'all')
|
3128
|
-
* @returns String
|
3129
|
-
* @short Converts full-width characters (zenkaku) to half-width (hankaku).
|
3130
|
-
* @extra [mode] accepts any combination of "a" (alphabet), "n" (numbers), "k" (katakana), "s" (spaces), "p" (punctuation), or "all".
|
3131
|
-
* @example
|
3132
|
-
*
|
3133
|
-
* 'タロウ YAMADAです!'.hankaku() -> 'タロウ YAMADAです!'
|
3134
|
-
* 'タロウ YAMADAです!'.hankaku('a') -> 'タロウ YAMADAです!'
|
3135
|
-
* 'タロウ YAMADAです!'.hankaku('alphabet') -> 'タロウ YAMADAです!'
|
3136
|
-
* 'タロウです! 25歳です!'.hankaku('katakana', 'numbers') -> 'タロウです! 25歳です!'
|
3137
|
-
* 'タロウです! 25歳です!'.hankaku('k', 'n') -> 'タロウです! 25歳です!'
|
3138
|
-
* 'タロウです! 25歳です!'.hankaku('kn') -> 'タロウです! 25歳です!'
|
3139
|
-
* 'タロウです! 25歳です!'.hankaku('sp') -> 'タロウです! 25歳です!'
|
3140
|
-
*
|
3141
|
-
***/
|
3142
|
-
'hankaku': function() {
|
3143
|
-
return convertCharacterWidth(this, arguments, allZenkaku, HankakuTable);
|
3144
|
-
},
|
3145
|
-
|
3146
|
-
/***
|
3147
|
-
* @method zenkaku([mode] = 'all')
|
3148
|
-
* @returns String
|
3149
|
-
* @short Converts half-width characters (hankaku) to full-width (zenkaku).
|
3150
|
-
* @extra [mode] accepts any combination of "a" (alphabet), "n" (numbers), "k" (katakana), "s" (spaces), "p" (punctuation), or "all".
|
3151
|
-
* @example
|
3152
|
-
*
|
3153
|
-
* 'タロウ YAMADAです!'.zenkaku() -> 'タロウ YAMADAです!'
|
3154
|
-
* 'タロウ YAMADAです!'.zenkaku('a') -> 'タロウ YAMADAです!'
|
3155
|
-
* 'タロウ YAMADAです!'.zenkaku('alphabet') -> 'タロウ YAMADAです!'
|
3156
|
-
* 'タロウです! 25歳です!'.zenkaku('katakana', 'numbers') -> 'タロウです! 25歳です!'
|
3157
|
-
* 'タロウです! 25歳です!'.zenkaku('k', 'n') -> 'タロウです! 25歳です!'
|
3158
|
-
* 'タロウです! 25歳です!'.zenkaku('kn') -> 'タロウです! 25歳です!'
|
3159
|
-
* 'タロウです! 25歳です!'.zenkaku('sp') -> 'タロウです! 25歳です!'
|
3160
|
-
*
|
3161
|
-
***/
|
3162
|
-
'zenkaku': function() {
|
3163
|
-
return convertCharacterWidth(this, arguments, allHankaku, ZenkakuTable);
|
3164
|
-
},
|
3165
|
-
|
3166
|
-
/***
|
3167
|
-
* @method hiragana([all] = true)
|
3168
|
-
* @returns String
|
3169
|
-
* @short Converts katakana into hiragana.
|
3170
|
-
* @extra If [all] is false, only full-width katakana will be converted.
|
3171
|
-
* @example
|
3172
|
-
*
|
3173
|
-
* 'カタカナ'.hiragana() -> 'かたかな'
|
3174
|
-
* 'コンニチハ'.hiragana() -> 'こんにちは'
|
3175
|
-
* 'カタカナ'.hiragana() -> 'かたかな'
|
3176
|
-
* 'カタカナ'.hiragana(false) -> 'カタカナ'
|
3177
|
-
*
|
3178
|
-
***/
|
3179
|
-
'hiragana': function(all) {
|
3180
|
-
var str = this;
|
3181
|
-
if(all !== false) {
|
3182
|
-
str = str.zenkaku('k');
|
3183
|
-
}
|
3184
|
-
return str.replace(/[\u30A1-\u30F6]/g, function(c) {
|
3185
|
-
return c.shift(-96);
|
3186
|
-
});
|
3187
|
-
},
|
3188
|
-
|
3189
|
-
/***
|
3190
|
-
* @method katakana()
|
3191
|
-
* @returns String
|
3192
|
-
* @short Converts hiragana into katakana.
|
3193
|
-
* @example
|
3194
|
-
*
|
3195
|
-
* 'かたかな'.katakana() -> 'カタカナ'
|
3196
|
-
* 'こんにちは'.katakana() -> 'コンニチハ'
|
3197
|
-
*
|
3198
|
-
***/
|
3199
|
-
'katakana': function() {
|
3200
|
-
return this.replace(/[\u3041-\u3096]/g, function(c) {
|
3201
|
-
return c.shift(96);
|
3202
|
-
});
|
3203
|
-
},
|
3204
|
-
|
3205
|
-
/***
|
3206
|
-
* @method toNumber([base] = 10)
|
3207
|
-
* @returns Number
|
3208
|
-
* @short Converts the string into a number.
|
3209
|
-
* @extra Any value with a "." fill be converted to a floating point value, otherwise an integer.
|
3210
|
-
* @example
|
3211
|
-
*
|
3212
|
-
* '153'.toNumber() -> 153
|
3213
|
-
* '12,000'.toNumber() -> 12000
|
3214
|
-
* '10px'.toNumber() -> 10
|
3215
|
-
* 'ff'.toNumber(16) -> 255
|
3216
|
-
*
|
3217
|
-
***/
|
3218
|
-
'toNumber': function(base) {
|
3219
|
-
var str = this.replace(/,/g, '');
|
3220
|
-
return str.match(/\./) ? parseFloat(str) : parseInt(str, base || 10);
|
3221
|
-
},
|
3222
|
-
|
3223
|
-
/***
|
3224
|
-
* @method reverse()
|
3225
|
-
* @returns String
|
3226
|
-
* @short Reverses the string.
|
3227
|
-
* @example
|
3228
|
-
*
|
3229
|
-
* 'jumpy'.reverse() -> 'ypmuj'
|
3230
|
-
* 'lucky charms'.reverse() -> 'smrahc ykcul'
|
3231
|
-
*
|
3232
|
-
***/
|
3233
|
-
'reverse': function() {
|
3234
|
-
return this.split('').reverse().join('');
|
3235
|
-
},
|
3236
|
-
|
3237
|
-
/***
|
3238
|
-
* @method compact()
|
3239
|
-
* @returns String
|
3240
|
-
* @short Compacts all white space in the string to a single space and trims the ends.
|
3241
|
-
* @example
|
3242
|
-
*
|
3243
|
-
* 'too \n much \n space'.compact() -> 'too much space'
|
3244
|
-
* 'enough \n '.compact() -> 'enought'
|
3245
|
-
*
|
3246
|
-
***/
|
3247
|
-
'compact': function() {
|
3248
|
-
return this.trim().replace(/([\r\n\s ])+/g, function(match, whitespace){
|
3249
|
-
return whitespace === ' ' ? whitespace : ' ';
|
3250
|
-
});
|
3251
|
-
},
|
3252
|
-
|
3253
|
-
/***
|
3254
|
-
* @method at(<index>, [loop] = true)
|
3255
|
-
* @returns String or Array
|
3256
|
-
* @short Gets the character(s) at a given index.
|
3257
|
-
* @extra When [loop] is true, overshooting the end of the string (or the beginning) will begin counting from the other end. As an alternate syntax, passing multiple indexes will get the characters at those indexes.
|
3258
|
-
* @example
|
3259
|
-
*
|
3260
|
-
* 'jumpy'.at(0) -> 'j'
|
3261
|
-
* 'jumpy'.at(2) -> 'm'
|
3262
|
-
* 'jumpy'.at(5) -> 'j'
|
3263
|
-
* 'jumpy'.at(5, false) -> ''
|
3264
|
-
* 'jumpy'.at(-1) -> 'y'
|
3265
|
-
* 'luckly charms'.at(1,3,5,7) -> ['u','k','y',c']
|
3266
|
-
*
|
3267
|
-
***/
|
3268
|
-
'at': function() {
|
3269
|
-
return entryAtIndex(this, arguments, true);
|
3270
|
-
},
|
3271
|
-
|
3272
|
-
/***
|
3273
|
-
* @method first([n] = 1)
|
3274
|
-
* @returns String
|
3275
|
-
* @short Returns the first [n] characters of the string.
|
3276
|
-
* @example
|
3277
|
-
*
|
3278
|
-
* 'lucky charms'.first() -> 'l'
|
3279
|
-
* 'lucky charms'.first(3) -> 'luc'
|
3280
|
-
*
|
3281
|
-
***/
|
3282
|
-
'first': function(num) {
|
3283
|
-
if(isUndefined(num)) num = 1;
|
3284
|
-
return this.substr(0, num);
|
3285
|
-
},
|
3286
|
-
|
3287
|
-
/***
|
3288
|
-
* @method last([n] = 1)
|
3289
|
-
* @returns String
|
3290
|
-
* @short Returns the last [n] characters of the string.
|
3291
|
-
* @example
|
3292
|
-
*
|
3293
|
-
* 'lucky charms'.last() -> 's'
|
3294
|
-
* 'lucky charms'.last(3) -> 'rms'
|
3295
|
-
*
|
3296
|
-
***/
|
3297
|
-
'last': function(num) {
|
3298
|
-
if(isUndefined(num)) num = 1;
|
3299
|
-
var start = this.length - num < 0 ? 0 : this.length - num;
|
3300
|
-
return this.substr(start);
|
3301
|
-
},
|
3302
|
-
|
3303
|
-
/***
|
3304
|
-
* @method from([index] = 0)
|
3305
|
-
* @returns String
|
3306
|
-
* @short Returns a section of the string starting from [index].
|
3307
|
-
* @example
|
3308
|
-
*
|
3309
|
-
* 'lucky charms'.from() -> 'lucky charms'
|
3310
|
-
* 'lucky charms'.from(7) -> 'harms'
|
3311
|
-
*
|
3312
|
-
***/
|
3313
|
-
'from': function(num) {
|
3314
|
-
return this.slice(num);
|
3315
|
-
},
|
3316
|
-
|
3317
|
-
/***
|
3318
|
-
* @method to([index] = end)
|
3319
|
-
* @returns String
|
3320
|
-
* @short Returns a section of the string ending at [index].
|
3321
|
-
* @example
|
3322
|
-
*
|
3323
|
-
* 'lucky charms'.to() -> 'lucky charms'
|
3324
|
-
* 'lucky charms'.to(7) -> 'lucky ch'
|
3325
|
-
*
|
3326
|
-
***/
|
3327
|
-
'to': function(num) {
|
3328
|
-
if(isUndefined(num)) num = this.length;
|
3329
|
-
return this.slice(0, num);
|
3330
|
-
},
|
3331
|
-
|
3332
|
-
/***
|
3333
|
-
* @method toDate([locale])
|
3334
|
-
* @returns Date
|
3335
|
-
* @short Creates a date from the string.
|
3336
|
-
* @extra Accepts a wide range of input. [locale] allows you to specify a locale code. See @date_format for more information.
|
3337
|
-
* @example
|
3338
|
-
*
|
3339
|
-
* 'January 25, 2015'.toDate() -> same as Date.create('January 25, 2015')
|
3340
|
-
* 'yesterday'.toDate() -> same as Date.create('yesterday')
|
3341
|
-
* 'next Monday'.toDate() -> same as Date.create('next Monday')
|
3342
|
-
*
|
3343
|
-
***/
|
3344
|
-
'toDate': function(locale) {
|
3345
|
-
var str = this.toString();
|
3346
|
-
return date.create ? date.create(str, locale) : new date(str);
|
3347
|
-
},
|
3348
|
-
|
3349
|
-
/***
|
3350
|
-
* @method dasherize()
|
3351
|
-
* @returns String
|
3352
|
-
* @short Converts underscores and camel casing to hypens.
|
3353
|
-
* @example
|
3354
|
-
*
|
3355
|
-
* 'a_farewell_to_arms'.dasherize() -> 'a-farewell-to-arms'
|
3356
|
-
* 'capsLock'.dasherize() -> 'caps-lock'
|
3357
|
-
*
|
3358
|
-
***/
|
3359
|
-
'dasherize': function() {
|
3360
|
-
return this.underscore().replace(/_/g, '-');
|
3361
|
-
},
|
3362
|
-
|
3363
|
-
/***
|
3364
|
-
* @method underscore()
|
3365
|
-
* @returns String
|
3366
|
-
* @short Converts hyphens and camel casing to underscores.
|
3367
|
-
* @example
|
3368
|
-
*
|
3369
|
-
* 'a-farewell-to-arms'.underscore() -> 'a_farewell_to_arms'
|
3370
|
-
* 'capsLock'.underscore() -> 'caps_lock'
|
3371
|
-
*
|
3372
|
-
***/
|
3373
|
-
'underscore': function() {
|
3374
|
-
return this
|
3375
|
-
.replace(/[-\s]+/g, '_')
|
3376
|
-
.replace(String.Inflector && String.Inflector.acronymRegExp, function(acronym, index) {
|
3377
|
-
return (index > 0 ? '_' : '') + acronym.toLowerCase();
|
3378
|
-
})
|
3379
|
-
.replace(/([A-Z\d]+)([A-Z][a-z])/g,'$1_$2')
|
3380
|
-
.replace(/([a-z\d])([A-Z])/g,'$1_$2')
|
3381
|
-
.toLowerCase();
|
3382
|
-
},
|
3383
|
-
|
3384
|
-
/***
|
3385
|
-
* @method camelize([first] = true)
|
3386
|
-
* @returns String
|
3387
|
-
* @short Converts underscores and hyphens to camel case. If [first] is true the first letter will also be capitalized.
|
3388
|
-
* @example
|
3389
|
-
*
|
3390
|
-
* 'caps_lock'.camelize() -> 'CapsLock'
|
3391
|
-
* 'moz-border-radius'.camelize() -> 'MozBorderRadius'
|
3392
|
-
* 'moz-border-radius'.camelize(false) -> 'mozBorderRadius'
|
3393
|
-
*
|
3394
|
-
***/
|
3395
|
-
'camelize': function(first) {
|
3396
|
-
return this.underscore().replace(/(^|_)([^_]+)/g, function(match, pre, word, index) {
|
3397
|
-
var acronym = getAcronym(word), capitalize = first !== false || index > 0;
|
3398
|
-
if(acronym) return capitalize ? acronym : acronym.toLowerCase();
|
3399
|
-
return capitalize ? word.capitalize() : word;
|
3400
|
-
});
|
3401
|
-
},
|
3402
|
-
|
3403
|
-
/***
|
3404
|
-
* @method spacify()
|
3405
|
-
* @returns String
|
3406
|
-
* @short Converts camel case, underscores, and hyphens to a properly spaced string.
|
3407
|
-
* @example
|
3408
|
-
*
|
3409
|
-
* 'camelCase'.spacify() -> 'camel case'
|
3410
|
-
* 'an-ugly-string'.spacify() -> 'an ugly string'
|
3411
|
-
* 'oh-no_youDid-not'.spacify().capitalize(true) -> 'something else'
|
3412
|
-
*
|
3413
|
-
***/
|
3414
|
-
'spacify': function() {
|
3415
|
-
return this.underscore().replace(/_/g, ' ');
|
3416
|
-
},
|
3417
|
-
|
3418
|
-
/***
|
3419
|
-
* @method stripTags([tag1], [tag2], ...)
|
3420
|
-
* @returns String
|
3421
|
-
* @short Strips all HTML tags from the string.
|
3422
|
-
* @extra Tags to strip may be enumerated in the parameters, otherwise will strip all.
|
3423
|
-
* @example
|
3424
|
-
*
|
3425
|
-
* '<p>just <b>some</b> text</p>'.stripTags() -> 'just some text'
|
3426
|
-
* '<p>just <b>some</b> text</p>'.stripTags('p') -> 'just <b>some</b> text'
|
3427
|
-
*
|
3428
|
-
***/
|
3429
|
-
'stripTags': function() {
|
3430
|
-
var str = this, args = arguments.length > 0 ? arguments : [''];
|
3431
|
-
multiArgs(args, function(tag) {
|
3432
|
-
str = str.replace(regexp('<\/?' + tag.escapeRegExp() + '[^<>]*>', 'gi'), '');
|
3433
|
-
});
|
3434
|
-
return str;
|
3435
|
-
},
|
3436
|
-
|
3437
|
-
/***
|
3438
|
-
* @method removeTags([tag1], [tag2], ...)
|
3439
|
-
* @returns String
|
3440
|
-
* @short Removes all HTML tags and their contents from the string.
|
3441
|
-
* @extra Tags to remove may be enumerated in the parameters, otherwise will remove all.
|
3442
|
-
* @example
|
3443
|
-
*
|
3444
|
-
* '<p>just <b>some</b> text</p>'.removeTags() -> ''
|
3445
|
-
* '<p>just <b>some</b> text</p>'.removeTags('b') -> '<p>just text</p>'
|
3446
|
-
*
|
3447
|
-
***/
|
3448
|
-
'removeTags': function() {
|
3449
|
-
var str = this, args = arguments.length > 0 ? arguments : ['\\S+'];
|
3450
|
-
multiArgs(args, function(t) {
|
3451
|
-
var reg = regexp('<(' + t + ')[^<>]*(?:\\/>|>.*?<\\/\\1>)', 'gi');
|
3452
|
-
str = str.replace(reg, '');
|
3453
|
-
});
|
3454
|
-
return str;
|
3455
|
-
},
|
3456
|
-
|
3457
|
-
/***
|
3458
|
-
* @method truncate(<length>, [split] = true, from = 'right', [ellipsis] = '...')
|
3459
|
-
* @returns Object
|
3460
|
-
* @short Truncates a string.
|
3461
|
-
* @extra If [split] is %false%, will not split words up, and instead discard the word where the truncation occurred. [from] can also be %"middle"% or %"left"%.
|
3462
|
-
* @example
|
3463
|
-
*
|
3464
|
-
* 'just sittin on the dock of the bay'.truncate(20) -> 'just sittin on the do...'
|
3465
|
-
* 'just sittin on the dock of the bay'.truncate(20, false) -> 'just sittin on the...'
|
3466
|
-
* 'just sittin on the dock of the bay'.truncate(20, true, 'middle') -> 'just sitt...of the bay'
|
3467
|
-
* 'just sittin on the dock of the bay'.truncate(20, true, 'middle') -> '...the dock of the bay'
|
3468
|
-
*
|
3469
|
-
***/
|
3470
|
-
'truncate': function(length, split, from, ellipsis) {
|
3471
|
-
var pos,
|
3472
|
-
prepend = '',
|
3473
|
-
append = '',
|
3474
|
-
str = this.toString(),
|
3475
|
-
chars = '[' + getTrimmableCharacters() + ']+',
|
3476
|
-
space = '[^' + getTrimmableCharacters() + ']*',
|
3477
|
-
reg = regexp(chars + space + '$');
|
3478
|
-
ellipsis = isUndefined(ellipsis) ? '...' : string(ellipsis);
|
3479
|
-
if(str.length <= length) {
|
3480
|
-
return str;
|
3481
|
-
}
|
3482
|
-
switch(from) {
|
3483
|
-
case 'left':
|
3484
|
-
pos = str.length - length;
|
3485
|
-
prepend = ellipsis;
|
3486
|
-
str = str.slice(pos);
|
3487
|
-
reg = regexp('^' + space + chars);
|
3488
|
-
break;
|
3489
|
-
case 'middle':
|
3490
|
-
pos = Math.floor(length / 2);
|
3491
|
-
append = ellipsis + str.slice(str.length - pos).trimLeft();
|
3492
|
-
str = str.slice(0, pos);
|
3493
|
-
break;
|
3494
|
-
default:
|
3495
|
-
pos = length;
|
3496
|
-
append = ellipsis;
|
3497
|
-
str = str.slice(0, pos);
|
3498
|
-
}
|
3499
|
-
if(split === false && this.slice(pos, pos + 1).match(/\S/)) {
|
3500
|
-
str = str.remove(reg);
|
3501
|
-
}
|
3502
|
-
return prepend + str + append;
|
3503
|
-
},
|
3504
|
-
|
3505
|
-
/***
|
3506
|
-
* @method assign(<obj1>, <obj2>, ...)
|
3507
|
-
* @returns String
|
3508
|
-
* @short Assigns variables to tokens in a string.
|
3509
|
-
* @extra If an object is passed, it's properties can be assigned using the object's keys. If a non-object (string, number, etc.) is passed it can be accessed by the argument number beginning with 1 (as with regex tokens). Multiple objects can be passed and will be merged together.
|
3510
|
-
* @example
|
3511
|
-
*
|
3512
|
-
* 'Welcome, Mr. {name}.'.assign({ name: 'Franklin' }) -> 'Welcome, Mr. Franklin.'
|
3513
|
-
* 'You are {1} years old today.'.assign(14) -> 'You are 14 years old today.'
|
3514
|
-
* '{n} and {r}'.assign({ n: 'Cheech' }, { r: 'Chong' }) -> 'Cheech and Chong'
|
3515
|
-
*
|
3516
|
-
***/
|
3517
|
-
'assign': function() {
|
3518
|
-
var assign = object.extended();
|
3519
|
-
multiArgs(arguments, function(a, i) {
|
3520
|
-
if(object.isObject(a)) {
|
3521
|
-
assign.merge(a);
|
3522
|
-
} else {
|
3523
|
-
assign[i + 1] = a;
|
3524
|
-
}
|
3525
|
-
});
|
3526
|
-
return this.replace(/\{(.+?)\}/g, function(m, key) {
|
3527
|
-
return hasOwnProperty(assign, key) ? assign[key] : m;
|
3528
|
-
});
|
3529
|
-
}
|
3530
|
-
|
3531
|
-
});
|
3532
|
-
|
3533
|
-
|
3534
|
-
extend(string, true, function(s) { return object.isRegExp(s); }, {
|
3535
|
-
|
3536
|
-
/*
|
3537
|
-
* Many thanks to Steve Levithan here for a ton of inspiration and work dealing with
|
3538
|
-
* cross browser Regex splitting. http://blog.stevenlevithan.com/archives/cross-browser-split
|
3539
|
-
*/
|
3540
|
-
|
3541
|
-
/***
|
3542
|
-
* @method split([separator], [limit])
|
3543
|
-
* @returns Array
|
3544
|
-
* @short Splits the string by [separator] into an Array.
|
3545
|
-
* @extra This method is native to Javascript, but Sugar patches it to provide cross-browser reliability when splitting on a regex.
|
3546
|
-
* @example
|
3547
|
-
*
|
3548
|
-
* 'comma,separated,values'.split(',') -> ['comma','separated','values']
|
3549
|
-
* 'a,b|c>d'.split(/[,|>]/) -> ['multi','separated','values']
|
3550
|
-
*
|
3551
|
-
***/
|
3552
|
-
'split': function(separator, limit) {
|
3553
|
-
var output = [];
|
3554
|
-
var lastLastIndex = 0;
|
3555
|
-
var separator = regexp(separator).addFlag('g'); // make `global` and avoid `lastIndex` issues by working with a copy
|
3556
|
-
var separator2, match, lastIndex, lastLength;
|
3557
|
-
if(!regexp.NPCGSupport) {
|
3558
|
-
separator2 = RegExp("^" + separator.source + "$(?!\\s)", separator.getFlags()); // doesn't need /g or /y, but they don't hurt
|
3559
|
-
}
|
3560
|
-
if(isUndefined(limit) || limit < 0) {
|
3561
|
-
limit = Infinity;
|
3562
|
-
} else {
|
3563
|
-
limit = limit | 0;
|
3564
|
-
if(!limit) return [];
|
3565
|
-
}
|
3566
|
-
|
3567
|
-
while (match = separator.exec(this)) {
|
3568
|
-
lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
|
3569
|
-
if(lastIndex > lastLastIndex) {
|
3570
|
-
output.push(this.slice(lastLastIndex, match.index));
|
3571
|
-
// fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
|
3572
|
-
if(!regexp.NPCGSupport && match.length > 1) {
|
3573
|
-
match[0].replace(separator2, function () {
|
3574
|
-
for (var i = 1; i < arguments.length - 2; i++) {
|
3575
|
-
if(isUndefined(arguments[i])) {
|
3576
|
-
match[i] = Undefined;
|
3577
|
-
}
|
3578
|
-
}
|
3579
|
-
});
|
3580
|
-
}
|
3581
|
-
if(match.length > 1 && match.index < this.length) {
|
3582
|
-
array.prototype.push.apply(output, match.slice(1));
|
3583
|
-
}
|
3584
|
-
lastLength = match[0].length;
|
3585
|
-
lastLastIndex = lastIndex;
|
3586
|
-
if(output.length >= limit) {
|
3587
|
-
break;
|
3588
|
-
}
|
3589
|
-
}
|
3590
|
-
if(separator.lastIndex === match.index) {
|
3591
|
-
separator.lastIndex++; // avoid an infinite loop
|
3592
|
-
}
|
3593
|
-
}
|
3594
|
-
if(lastLastIndex === this.length) {
|
3595
|
-
if(lastLength || !separator.test('')) output.push('');
|
3596
|
-
} else {
|
3597
|
-
output.push(this.slice(lastLastIndex));
|
3598
|
-
}
|
3599
|
-
return output.length > limit ? output.slice(0, limit) : output;
|
3600
|
-
}
|
3601
|
-
|
3602
|
-
});
|
3603
|
-
|
3604
|
-
|
3605
|
-
|
3606
|
-
|
3607
|
-
// Aliases
|
3608
|
-
|
3609
|
-
extend(string, true, false, {
|
3610
|
-
|
3611
|
-
/***
|
3612
|
-
* @method insert()
|
3613
|
-
* @alias add
|
3614
|
-
*
|
3615
|
-
***/
|
3616
|
-
'insert': string.prototype.add
|
3617
|
-
});
|
3618
|
-
|
3619
|
-
|
3620
|
-
|
3621
|
-
|
3622
|
-
|
3623
|
-
|
3624
|
-
/***
|
3625
|
-
* RegExp module
|
3626
|
-
*
|
3627
|
-
* Note here that methods on the RegExp class like .exec and .test will fail in the current version of SpiderMonkey being
|
3628
|
-
* used by CouchDB when using shorthand regex notation like /foo/. This is the reason for the intermixed use of shorthand
|
3629
|
-
* and compiled regexes here. If you're using JS in CouchDB, it is safer to ALWAYS compile your regexes from a string.
|
3630
|
-
*
|
3631
|
-
***/
|
3632
|
-
|
3633
|
-
regexp.NPCGSupport = isUndefined(regexp('()??').exec('')[1]); // NPCG: nonparticipating capturing group
|
3634
|
-
|
3635
|
-
function getFlags(reg, flag) {
|
3636
|
-
var flags = '';
|
3637
|
-
if(flag == 'g' || reg.global) flags += 'g';
|
3638
|
-
if(flag == 'i' || reg.ignoreCase) flags += 'i';
|
3639
|
-
if(flag == 'm' || reg.multiline) flags += 'm';
|
3640
|
-
if(flag == 'y' || reg.sticky) flags += 'y';
|
3641
|
-
return flags;
|
3642
|
-
}
|
3643
|
-
|
3644
|
-
extend(regexp, false, false, {
|
3645
|
-
|
3646
|
-
/***
|
3647
|
-
* @method RegExp.escape(<str> = '')
|
3648
|
-
* @returns String
|
3649
|
-
* @short Escapes all RegExp tokens in a string.
|
3650
|
-
* @example
|
3651
|
-
*
|
3652
|
-
* RegExp.escape('really?') -> 'really\?'
|
3653
|
-
* RegExp.escape('yes.') -> 'yes\.'
|
3654
|
-
* RegExp.escape('(not really)') -> '\(not really\)'
|
3655
|
-
*
|
3656
|
-
***/
|
3657
|
-
'escape': function(str) {
|
3658
|
-
if(!object.isString(str)) str = String(str);
|
3659
|
-
return str.replace(/([\\/'*+?|()\[\]{}.^$])/g,'\\$1');
|
3660
|
-
}
|
3661
|
-
|
3662
|
-
});
|
3663
|
-
|
3664
|
-
extend(regexp, true, false, {
|
3665
|
-
|
3666
|
-
/***
|
3667
|
-
* @method getFlags()
|
3668
|
-
* @returns String
|
3669
|
-
* @short Returns the flags of the regex as a string.
|
3670
|
-
* @example
|
3671
|
-
*
|
3672
|
-
* /texty/gim.getFlags('testy') -> 'gim'
|
3673
|
-
*
|
3674
|
-
***/
|
3675
|
-
'getFlags': function() {
|
3676
|
-
return getFlags(this);
|
3677
|
-
},
|
3678
|
-
|
3679
|
-
/***
|
3680
|
-
* @method setFlags(<flags>)
|
3681
|
-
* @returns RegExp
|
3682
|
-
* @short Sets the flags on a regex and retuns a copy.
|
3683
|
-
* @example
|
3684
|
-
*
|
3685
|
-
* /texty/.setFlags('gim') -> now has global, ignoreCase, and multiline set
|
3686
|
-
*
|
3687
|
-
***/
|
3688
|
-
'setFlags': function(flags) {
|
3689
|
-
return regexp(this.source, flags);
|
3690
|
-
},
|
3691
|
-
|
3692
|
-
/***
|
3693
|
-
* @method addFlag(<flag>)
|
3694
|
-
* @returns RegExp
|
3695
|
-
* @short Adds <flag> to the regex.
|
3696
|
-
* @example
|
3697
|
-
*
|
3698
|
-
* /texty/.addFlag('g') -> now has global flag set
|
3699
|
-
*
|
3700
|
-
***/
|
3701
|
-
'addFlag': function(flag) {
|
3702
|
-
return this.setFlags(getFlags(this, flag));
|
3703
|
-
},
|
3704
|
-
|
3705
|
-
/***
|
3706
|
-
* @method removeFlag(<flag>)
|
3707
|
-
* @returns RegExp
|
3708
|
-
* @short Removes <flag> from the regex.
|
3709
|
-
* @example
|
3710
|
-
*
|
3711
|
-
* /texty/g.removeFlag('g') -> now has global flag removed
|
3712
|
-
*
|
3713
|
-
***/
|
3714
|
-
'removeFlag': function(flag) {
|
3715
|
-
return this.setFlags(getFlags(this).replace(flag, ''));
|
3716
|
-
}
|
3717
|
-
|
3718
|
-
});
|
3719
|
-
|
3720
|
-
|
3721
|
-
|
3722
|
-
|
3723
|
-
/***
|
3724
|
-
* Function module
|
3725
|
-
*
|
3726
|
-
***/
|
3727
|
-
|
3728
|
-
function setDelay(fn, ms, after, scope, args) {
|
3729
|
-
if(!fn.timers) fn.timers = [];
|
3730
|
-
if(!object.isNumber(ms)) ms = 0;
|
3731
|
-
fn.timers.push(setTimeout(function(){
|
3732
|
-
fn.timers.removeAt(index);
|
3733
|
-
after.apply(scope, args || []);
|
3734
|
-
}, ms));
|
3735
|
-
var index = fn.timers.length;
|
3736
|
-
}
|
3737
|
-
|
3738
|
-
function buildBind() {
|
3739
|
-
var support = false;
|
3740
|
-
if(Function.prototype.bind) {
|
3741
|
-
function F() {};
|
3742
|
-
var B = F.bind();
|
3743
|
-
support = (new B instanceof B) && !(new F instanceof B);
|
3744
|
-
}
|
3745
|
-
extend(Function, true, !support, {
|
3746
|
-
|
3747
|
-
/***
|
3748
|
-
* @method bind(<scope>, [arg1], ...)
|
3749
|
-
* @returns Function
|
3750
|
-
* @short Binds <scope> as the %this% object for the function when it is called. Also allows currying an unlimited number of parameters.
|
3751
|
-
* @extra "currying" means setting parameters ([arg1], [arg2], etc.) ahead of time so that they are passed when the function is called later. If you pass additional parameters when the function is actually called, they will be added will be added to the end of the curried parameters.
|
3752
|
-
* @example
|
3753
|
-
*
|
3754
|
-
+ (function() {
|
3755
|
-
* return this;
|
3756
|
-
* }).bind('woof')(); -> returns 'woof'; function is bound with 'woof' as the this object.
|
3757
|
-
* (function(a) {
|
3758
|
-
* return a;
|
3759
|
-
* }).bind(1, 2)(); -> returns 2; function is bound with 1 as the this object and 2 curried as the first parameter
|
3760
|
-
* (function(a, b) {
|
3761
|
-
* return a + b;
|
3762
|
-
* }).bind(1, 2)(3); -> returns 5; function is bound with 1 as the this object, 2 curied as the first parameter and 3 passed as the second when calling the function
|
3763
|
-
*
|
3764
|
-
***/
|
3765
|
-
'bind': function(scope) {
|
3766
|
-
var fn = this, args = getArgs(arguments, 1), nop, bound;
|
3767
|
-
if(!object.isFunction(this)) {
|
3768
|
-
throw new TypeError('Function.prototype.bind called on a non-function');
|
3769
|
-
}
|
3770
|
-
bound = function() {
|
3771
|
-
return fn.apply(fn.prototype && this instanceof fn ? this : scope, args.concat(getArgs(arguments)));
|
3772
|
-
}
|
3773
|
-
nop = function() {};
|
3774
|
-
nop.prototype = this.prototype;
|
3775
|
-
bound.prototype = new nop();
|
3776
|
-
return bound;
|
3777
|
-
}
|
3778
|
-
|
3779
|
-
});
|
3780
|
-
}
|
3781
|
-
|
3782
|
-
function buildFunction() {
|
3783
|
-
buildBind();
|
3784
|
-
}
|
3785
|
-
|
3786
|
-
|
3787
|
-
extend(Function, true, false, {
|
3788
|
-
|
3789
|
-
/***
|
3790
|
-
* @method lazy([ms] = 1, [limit] = Infinity)
|
3791
|
-
* @returns Function
|
3792
|
-
* @short Creates lazy functions for non-blocking operations.
|
3793
|
-
* @extra This method will wrap the function inside another that, when executed repeatedly in a loop, will execute [ms] milliseconds after the last iteration (a.k.a. "function throttling"). By passing in a smaller value for [ms] (can be a decimal < 1), you can "tighen up" the execution time so that the iterations happen faster. By passing in a larger value for [ms], you can space the function execution out to prevent thread blocking. Playing with this number is the easiest way to strike a balance for heavier operations. Calls to lazy functions beyond [limit], if it is set to a finite number, will be ignored if other calls are waiting. For example if [limit] is 50 and 50 calls are queued, any subsequent call will be ignored until the number of queued calls goes down to < 50 again. This prevents lazy functions from being hammered too hard. Additionally, lazy functions can be canceled during execution using the %cancel% method, which will clear the entire queue.
|
3794
|
-
* @example
|
3795
|
-
*
|
3796
|
-
* (function() {
|
3797
|
-
* // Executes immediately.
|
3798
|
-
* }).lazy()();
|
3799
|
-
* (3).times(function() {
|
3800
|
-
* // Executes 3 times, with each execution 20ms later than the last.
|
3801
|
-
* }.lazy(20));
|
3802
|
-
* (100).times(function() {
|
3803
|
-
* // Executes 50 times, with each execution 20ms later than the last.
|
3804
|
-
* }.lazy(20, 50));
|
3805
|
-
*
|
3806
|
-
***/
|
3807
|
-
'lazy': function(ms, limit) {
|
3808
|
-
var fn = this, queue = [], lock = false, execute, rounded, perExecution;
|
3809
|
-
ms = ms || 1;
|
3810
|
-
limit = limit || Infinity;
|
3811
|
-
rounded = ms.ceil();
|
3812
|
-
perExecution = round(rounded / ms);
|
3813
|
-
execute = function() {
|
3814
|
-
if(lock || queue.length == 0) return;
|
3815
|
-
var max = Math.max(queue.length - perExecution, 0);
|
3816
|
-
while(queue.length > max) {
|
3817
|
-
// Getting uber-meta here...
|
3818
|
-
Function.prototype.apply.apply(fn, queue.shift());
|
3819
|
-
}
|
3820
|
-
setDelay(lazy, rounded, function() {
|
3821
|
-
lock = false;
|
3822
|
-
execute();
|
3823
|
-
});
|
3824
|
-
lock = true;
|
3825
|
-
}
|
3826
|
-
function lazy() {
|
3827
|
-
// The first call is immediate, so having 1 in the queue
|
3828
|
-
// implies two calls have already taken place.
|
3829
|
-
if(lock && queue.length > limit - 2) return;
|
3830
|
-
queue.push([this, arguments]);
|
3831
|
-
execute();
|
3832
|
-
}
|
3833
|
-
return lazy;
|
3834
|
-
},
|
3835
|
-
|
3836
|
-
/***
|
3837
|
-
* @method delay([ms] = 1, [arg1], ...)
|
3838
|
-
* @returns Function
|
3839
|
-
* @short Executes the function after <ms> milliseconds.
|
3840
|
-
* @extra Returns a reference to itself. %delay% is also a way to execute non-blocking operations that will wait until the CPU is free. Delayed functions can be canceled using the %cancel% method. Can also curry arguments passed in after <ms>.
|
3841
|
-
* @example
|
3842
|
-
*
|
3843
|
-
* (function(arg1) {
|
3844
|
-
* // called 1s later
|
3845
|
-
* }).delay(1000, 'arg1');
|
3846
|
-
*
|
3847
|
-
***/
|
3848
|
-
'delay': function(ms) {
|
3849
|
-
var fn = this;
|
3850
|
-
var args = getArgs(arguments, 1);
|
3851
|
-
setDelay(fn, ms, fn, fn, args);
|
3852
|
-
return fn;
|
3853
|
-
},
|
3854
|
-
|
3855
|
-
/***
|
3856
|
-
* @method throttle(<ms>)
|
3857
|
-
* @returns Function
|
3858
|
-
* @short Creates a throttled version of the function that will only be executed once per <ms> milliseconds.
|
3859
|
-
* @example
|
3860
|
-
*
|
3861
|
-
* var fn = (function(arg1) {
|
3862
|
-
* // called immediately and will wait 50ms until it responds again
|
3863
|
-
* }).throttle(50); fn() fn() fn();
|
3864
|
-
*
|
3865
|
-
***/
|
3866
|
-
'throttle': function(ms) {
|
3867
|
-
return this.lazy(ms, 1);
|
3868
|
-
},
|
3869
|
-
|
3870
|
-
/***
|
3871
|
-
* @method debounce(<ms>)
|
3872
|
-
* @returns Function
|
3873
|
-
* @short Creates a "debounced" function that postpones its execution until after <ms> milliseconds have passed.
|
3874
|
-
* @extra This method is useful to execute a function after things have "settled down". A good example of this is when a user tabs quickly through form fields, execution of a heavy operation should happen after a few milliseconds when they have "settled" on a field.
|
3875
|
-
* @example
|
3876
|
-
*
|
3877
|
-
* var fn = (function(arg1) {
|
3878
|
-
* // called once 50ms later
|
3879
|
-
* }).debounce(50); fn() fn() fn();
|
3880
|
-
*
|
3881
|
-
***/
|
3882
|
-
'debounce': function(ms) {
|
3883
|
-
var fn = this;
|
3884
|
-
return function() {
|
3885
|
-
fn.cancel();
|
3886
|
-
setDelay(fn, ms, fn, this, arguments);
|
3887
|
-
}
|
3888
|
-
},
|
3889
|
-
|
3890
|
-
/***
|
3891
|
-
* @method cancel()
|
3892
|
-
* @returns Function
|
3893
|
-
* @short Cancels a delayed function scheduled to be run.
|
3894
|
-
* @extra %delay%, %lazy%, and %debounce% can all set delays. Note that this method won't work when using certain other frameworks like Prototype, as they will retain their %delay% method.
|
3895
|
-
* @example
|
3896
|
-
*
|
3897
|
-
* (function() {
|
3898
|
-
* alert('hay'); // Never called
|
3899
|
-
* }).delay(500).cancel();
|
3900
|
-
*
|
3901
|
-
***/
|
3902
|
-
'cancel': function() {
|
3903
|
-
if(object.isArray(this.timers)) {
|
3904
|
-
while(this.timers.length > 0) {
|
3905
|
-
clearTimeout(this.timers.shift());
|
3906
|
-
}
|
3907
|
-
}
|
3908
|
-
return this;
|
3909
|
-
},
|
3910
|
-
|
3911
|
-
/***
|
3912
|
-
* @method after([num] = 1)
|
3913
|
-
* @returns Function
|
3914
|
-
* @short Creates a function that will execute after [num] calls.
|
3915
|
-
* @extra %after% is useful for running a final callback after a series of asynchronous operations, when the order in which the operations will complete is unknown.
|
3916
|
-
* @example
|
3917
|
-
*
|
3918
|
-
* var fn = (function() {
|
3919
|
-
* // Will be executed once only
|
3920
|
-
* }).after(3); fn(); fn(); fn();
|
3921
|
-
*
|
3922
|
-
***/
|
3923
|
-
'after': function(num) {
|
3924
|
-
var fn = this, counter = 0, storedArguments = [];
|
3925
|
-
if(!object.isNumber(num)) {
|
3926
|
-
num = 1;
|
3927
|
-
} else if(num === 0) {
|
3928
|
-
fn.call();
|
3929
|
-
return fn;
|
3930
|
-
}
|
3931
|
-
return function() {
|
3932
|
-
var ret;
|
3933
|
-
storedArguments.push(Array.create(arguments));
|
3934
|
-
counter++;
|
3935
|
-
if(counter == num) {
|
3936
|
-
ret = fn.call(this, storedArguments);
|
3937
|
-
counter = 0;
|
3938
|
-
storedArguments = [];
|
3939
|
-
return ret;
|
3940
|
-
}
|
3941
|
-
}
|
3942
|
-
},
|
3943
|
-
|
3944
|
-
/***
|
3945
|
-
* @method once()
|
3946
|
-
* @returns Function
|
3947
|
-
* @short Creates a function that will execute only once and store the result.
|
3948
|
-
* @extra %once% is useful for creating functions that will cache the result of an expensive operation and use it on subsequent calls. Also it can be useful for creating initialization functions that only need to be run once.
|
3949
|
-
* @example
|
3950
|
-
*
|
3951
|
-
* var fn = (function() {
|
3952
|
-
* // Will be executed once only
|
3953
|
-
* }).once(); fn(); fn(); fn();
|
3954
|
-
*
|
3955
|
-
***/
|
3956
|
-
'once': function() {
|
3957
|
-
var fn = this;
|
3958
|
-
return function() {
|
3959
|
-
return hasOwnProperty(fn, 'memo') ? fn['memo'] : fn['memo'] = fn.apply(this, arguments);
|
3960
|
-
}
|
3961
|
-
},
|
3962
|
-
|
3963
|
-
/***
|
3964
|
-
* @method fill(<arg1>, <arg2>, ...)
|
3965
|
-
* @returns Function
|
3966
|
-
* @short Returns a new version of the function which when called will have some of its arguments pre-emptively filled in, also known as "currying".
|
3967
|
-
* @extra Arguments passed to a "filled" function are generally appended to the curried arguments. However, if %undefined% is passed as any of the arguments to %fill%, it will be replaced, when the "filled" function is executed. This allows currying of arguments even when they occur toward the end of an argument list (the example demonstrates this much more clearly).
|
3968
|
-
* @example
|
3969
|
-
*
|
3970
|
-
* var delayOneSecond = setTimeout.fill(undefined, 1000);
|
3971
|
-
* delayOneSecond(function() {
|
3972
|
-
* // Will be executed 1s later
|
3973
|
-
* });
|
3974
|
-
*
|
3975
|
-
***/
|
3976
|
-
'fill': function() {
|
3977
|
-
var fn = this, curried = getArgs(arguments);
|
3978
|
-
return function() {
|
3979
|
-
var args = getArgs(arguments);
|
3980
|
-
arrayEach(curried, function(arg, index) {
|
3981
|
-
if(arg != null || index >= args.length) args.splice(index, 0, arg);
|
3982
|
-
});
|
3983
|
-
return fn.apply(this, args);
|
3984
|
-
}
|
3985
|
-
}
|
3986
|
-
|
3987
|
-
|
3988
|
-
});
|
3989
|
-
|
3990
|
-
|
3991
|
-
// Initialize
|
3992
|
-
buildObject();
|
3993
|
-
buildString();
|
3994
|
-
buildFunction();
|
3995
|
-
buildArray();
|
3996
|
-
initializeClass(date);
|
3997
|
-
|
3998
|
-
Object.initializeClass = initializeClass;
|
3999
|
-
|
4000
|
-
|
4001
|
-
})();
|