diff --git a/Gruntfile.js b/Gruntfile.js
index 31f6e6bdc..0647eb59e 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -24,7 +24,7 @@ module.exports = function (grunt) {
'UI/JsLibraries/jquery.knob.js' : 'http://raw.github.com/aterrien/jQuery-Knob/master/js/jquery.knob.js',
'UI/JsLibraries/require.js' : 'http://raw.github.com/jrburke/requirejs/master/require.js',
- 'UI/JsLibraries/sugar.js' : 'http://raw.github.com/andrewplummer/Sugar/master/release/sugar-full.development.js',
+ 'UI/JsLibraries/filesize.js' : 'http://cdn.filesizejs.com/filesize.js',
'UI/JsLibraries/lodash.underscore.js' : 'http://raw.github.com/bestiejs/lodash/master/dist/lodash.underscore.js',
'UI/JsLibraries/messenger.js' : 'http://raw.github.com/HubSpot/messenger/master/build/js/messenger.js',
diff --git a/UI/Calendar/CalendarView.js b/UI/Calendar/CalendarView.js
index 6ba4f40cc..5a33ba980 100644
--- a/UI/Calendar/CalendarView.js
+++ b/UI/Calendar/CalendarView.js
@@ -4,10 +4,11 @@ define(
[
'app',
'marionette',
+ 'moment',
'Calendar/Collection',
'Episode/Layout',
'fullcalendar'
- ], function (App, Marionette, CalendarCollection, EpisodeLayout) {
+ ], function (App, Marionette, Moment, CalendarCollection, EpisodeLayout) {
var _instance;
@@ -50,8 +51,8 @@ define(
},
getEvents: function (start, end, callback) {
- var startDate = Date.create(start).format(Date.ISO8601_DATETIME);
- var endDate = Date.create(end).format(Date.ISO8601_DATETIME);
+ var startDate = Moment(start).toISOString();
+ var endDate = Moment(end).toISOString();
_instance.collection.fetch({
data : { start: startDate, end: endDate },
@@ -80,11 +81,11 @@ define(
getStatusLevel: function (element) {
var hasFile = element.get('hasFile');
- var currentTime = Date.create();
- var start = Date.create(element.get('airDate'));
- var end = Date.create(element.get('end'));
+ var currentTime = Moment();
+ var start = Moment(element.get('airDate'));
+ var end = Moment(element.get('end'));
- if (currentTime.isBetween(start, end)) {
+ if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
return 'warning';
}
diff --git a/UI/Calendar/UpcomingCollection.js b/UI/Calendar/UpcomingCollection.js
index ffabaa803..a7e3ba58a 100644
--- a/UI/Calendar/UpcomingCollection.js
+++ b/UI/Calendar/UpcomingCollection.js
@@ -2,20 +2,21 @@
define(
[
'backbone',
+ 'moment',
'Series/EpisodeModel'
- ], function (Backbone, EpisodeModel) {
+ ], function (Backbone, Moment, EpisodeModel) {
return Backbone.Collection.extend({
url : window.ApiRoot + '/calendar',
model: EpisodeModel,
comparator: function (model1, model2) {
var airDate1 = model1.get('airDate');
- var date1 = Date.create(airDate1);
- var time1 = date1.getTime();
+ var date1 = Moment(airDate1);
+ var time1 = date1.unix();
var airDate2 = model2.get('airDate');
- var date2 = Date.create(airDate2);
- var time2 = date2.getTime();
+ var date2 = Moment(airDate2);
+ var time2 = date2.unix();
if (time1 < time2){
return -1;
diff --git a/UI/Cells/AirDateCell.js b/UI/Cells/AirDateCell.js
index 9072fe22a..f48d40730 100644
--- a/UI/Cells/AirDateCell.js
+++ b/UI/Cells/AirDateCell.js
@@ -2,16 +2,24 @@
define(
[
'backgrid',
+ 'moment',
'Shared/FormatHelpers'
- ], function (Backgrid, FormatHelpers) {
+ ], function (Backgrid, Moment, FormatHelpers) {
return Backgrid.Cell.extend({
className: 'air-date-cell',
render: function () {
this.$el.empty();
- var airDate = this.model.get(this.column.get('name'));
- this.$el.html(FormatHelpers.DateHelper(airDate));
+ var date = this.model.get(this.column.get('name'));
+
+ if (date) {
+ this.$el.html(FormatHelpers.DateHelper(date));
+
+ //TODO: Figure out why this makes the series grid freak out
+ //this.$el.attr('title', Moment(date).format('LLLL'));
+ }
+
return this;
}
diff --git a/UI/Cells/EpisodeNumberCell.js b/UI/Cells/EpisodeNumberCell.js
index 6c982061b..f51589f51 100644
--- a/UI/Cells/EpisodeNumberCell.js
+++ b/UI/Cells/EpisodeNumberCell.js
@@ -2,8 +2,9 @@
define(
[
- 'Cells/NzbDroneCell'
- ], function (NzbDroneCell) {
+ 'Cells/NzbDroneCell',
+ 'Shared/FormatHelpers'
+ ], function (NzbDroneCell, FormatHelpers) {
return NzbDroneCell.extend({
className: 'episode-number-cell',
@@ -30,14 +31,14 @@ define(
if (episodes.constructor === Array) {
paddedEpisodes = _.map(episodes,function (episodeNumber) {
- return episodeNumber.pad(2);
+ return FormatHelpers.pad(episodeNumber, 2);
}).join();
}
else {
- paddedEpisodes = episodes.pad(2);
+ paddedEpisodes = FormatHelpers.pad(episodes, 2);
}
- result = 'S{0}-E{1}'.format(seasonNumber.pad(2), paddedEpisodes);
+ result = '{0}x{1}'.format(seasonNumber, paddedEpisodes);
}
else if (airDate) {
result = new Date(airDate).toLocaleDateString();
diff --git a/UI/Cells/EpisodeStatusCell.js b/UI/Cells/EpisodeStatusCell.js
index e1d0eb5a9..8d3b3d74b 100644
--- a/UI/Cells/EpisodeStatusCell.js
+++ b/UI/Cells/EpisodeStatusCell.js
@@ -2,8 +2,9 @@
define(
[
- 'backgrid'
- ], function (Backgrid) {
+ 'backgrid',
+ 'moment'
+ ], function (Backgrid, Moment) {
return Backgrid.Cell.extend({
className: 'episode-status-cell',
@@ -16,7 +17,7 @@ define(
var icon;
var tooltip;
- var hasAired = Date.create(this.model.get('airDate')).isBefore(Date.create());
+ var hasAired = Moment(this.model.get('airDate')).isBefore(Moment());
var hasFile = this.model.get('hasFile');
if (hasFile) {
diff --git a/UI/Cells/RelativeDateCell.js b/UI/Cells/RelativeDateCell.js
index 314aff53c..f8e4ea4ba 100644
--- a/UI/Cells/RelativeDateCell.js
+++ b/UI/Cells/RelativeDateCell.js
@@ -1,8 +1,10 @@
'use strict';
define(
[
- 'Cells/NzbDroneCell'
- ], function (NzbDroneCell) {
+ 'Cells/NzbDroneCell',
+ 'moment',
+ 'Shared/FormatHelpers'
+ ], function (NzbDroneCell, Moment, FormatHelpers) {
return NzbDroneCell.extend({
className: 'relative-date-cell',
@@ -10,7 +12,11 @@ define(
render: function () {
var date = this.model.get(this.column.get('name'));
- this.$el.html(Date.create(date).relative());
+
+ if (date) {
+ this.$el.html(FormatHelpers.DateHelper(date));
+ this.$el.attr('title', Moment(date).format('LLLL'));
+ }
return this;
}
diff --git a/UI/Cells/cells.less b/UI/Cells/cells.less
index e73de6580..724104c67 100644
--- a/UI/Cells/cells.less
+++ b/UI/Cells/cells.less
@@ -8,11 +8,13 @@
}
.air-date-cell {
- width : 100px;
+ width : 120px;
+ cursor: default;
}
.relative-date-cell {
width : 150px;
+ cursor: default;
}
.quality-cell {
diff --git a/UI/Handlebars/Helpers/DateTime.js b/UI/Handlebars/Helpers/DateTime.js
index 6a4513329..ac61cb63d 100644
--- a/UI/Handlebars/Helpers/DateTime.js
+++ b/UI/Handlebars/Helpers/DateTime.js
@@ -2,15 +2,27 @@
define(
[
'handlebars',
- 'sugar'
- ], function (Handlebars) {
+ 'moment',
+ 'Shared/FormatHelpers'
+ ], function (Handlebars, Moment, FormatHelpers) {
Handlebars.registerHelper('ShortDate', function (input) {
if (!input) {
return '';
}
- var date = Date.create(input);
- var result = '' + date.short() + '';
+ var date = Moment(input);
+ var result = '' + date.format('LL') + '';
+
+ return new Handlebars.SafeString(result);
+ });
+
+ Handlebars.registerHelper('NextAiring', function (input) {
+ if (!input) {
+ return '';
+ }
+
+ var date = Moment(input);
+ var result = '' + FormatHelpers.DateHelper(input) + '';
return new Handlebars.SafeString(result);
});
@@ -20,7 +32,7 @@ define(
return '';
}
- return Date.create(input).format('{dd}');
+ return Moment(input).format('DD');
});
Handlebars.registerHelper('Month', function (input) {
@@ -28,7 +40,7 @@ define(
return '';
}
- return Date.create(input).format('{Mon}');
+ return Moment(input).format('MMM');
});
Handlebars.registerHelper('StartTime', function (input) {
@@ -36,11 +48,11 @@ define(
return '';
}
- var date = Date.create(input);
- if (date.format('{mm}') === '00') {
- return date.format('{h}{tt}');
+ var date = Moment(input);
+ if (date.format('mm') === '00') {
+ return date.format('ha');
}
- return date.format('{h}.{mm}{tt}');
+ return date.format('h.mma');
});
});
diff --git a/UI/Handlebars/Helpers/Episode.js b/UI/Handlebars/Helpers/Episode.js
index 662368c41..5ce040299 100644
--- a/UI/Handlebars/Helpers/Episode.js
+++ b/UI/Handlebars/Helpers/Episode.js
@@ -2,8 +2,9 @@
define(
[
'handlebars',
- 'Shared/FormatHelpers'
- ], function (Handlebars, FormatHelpers) {
+ 'Shared/FormatHelpers',
+ 'moment'
+ ], function (Handlebars, FormatHelpers, Moment) {
Handlebars.registerHelper('EpisodeNumber', function () {
if (this.series.seriesType === 'daily') {
@@ -19,11 +20,11 @@ define(
Handlebars.registerHelper('StatusLevel', function () {
var hasFile = this.hasFile;
- var currentTime = Date.create();
- var start = Date.create(this.airDate);
- var end = Date.create(this.end);
+ var currentTime = Moment();
+ var start = Moment(this.airDate);
+ var end = Moment(this.end);
- if (currentTime.isBetween(start, end)) {
+ if (currentTime.isAfter(start) && currentTime.isBefore(end)) {
return 'warning';
}
diff --git a/UI/Handlebars/Helpers/Numbers.js b/UI/Handlebars/Helpers/Numbers.js
index 45ea021bf..3ede0e32d 100644
--- a/UI/Handlebars/Helpers/Numbers.js
+++ b/UI/Handlebars/Helpers/Numbers.js
@@ -9,6 +9,7 @@ define(
});
Handlebars.registerHelper('Pad2', function (input) {
- return input.pad(2);
+ return FormatHelpers.pad(input, 2);
});
+
});
diff --git a/UI/JsLibraries/filesize.js b/UI/JsLibraries/filesize.js
new file mode 100644
index 000000000..b6f7b24ab
--- /dev/null
+++ b/UI/JsLibraries/filesize.js
@@ -0,0 +1,153 @@
+/**
+ * filesize
+ *
+ * @author Jason Mulligan
+ * @copyright 2013 Jason Mulligan
+ * @license BSD-3
+ * @link http://filesizejs.com
+ * @module filesize
+ * @version 1.10.0
+ */
+( function ( global ) {
+ "use strict";
+
+ var base = 10,
+ right = /\.(.*)/,
+ bit = /b$/,
+ bite = /^B$/,
+ zero = /^0$/,
+ options;
+
+ options = {
+ all : {
+ increments : [["B", 1], ["kb", 125], ["kB", 1000], ["Mb", 125000], ["MB", 1000000], ["Gb", 125000000], ["GB", 1000000000], ["Tb", 125000000000], ["TB", 1000000000000], ["Pb", 125000000000000], ["PB", 1000000000000000]],
+ nth : 11
+ },
+ bitless : {
+ increments : [["B", 1], ["kB", 1000], ["MB", 1000000], ["GB", 1000000000], ["TB", 1000000000000], ["PB", 1000000000000000]],
+ nth : 6
+ }
+ };
+
+ /**
+ * filesize
+ *
+ * @param {Mixed} arg String, Int or Float to transform
+ * @param {Mixed} pos [Optional] Position to round to, defaults to 2 if shrt is ommitted, or `true` for shrthand output
+ * @param {Boolean} bits [Optional] Determines if `bit` sizes are used for result calculation, default is true
+ * @return {String} Readable file size String
+ */
+ function filesize ( arg) {
+ var result = "",
+ bits = true,
+ skip = false,
+ i, neg, num, pos, shrt, size, sizes, suffix, z;
+
+ // Determining arguments
+ if (arguments[3] !== undefined) {
+ pos = arguments[1];
+ shrt = arguments[2];
+ bits = arguments[3];
+ }
+ else {
+ typeof arguments[1] === "boolean" ? shrt = arguments[1] : pos = arguments[1];
+
+ if ( typeof arguments[2] === "boolean" ) {
+ bits = arguments[2];
+ }
+ }
+
+ if ( isNaN( arg ) || ( pos !== undefined && isNaN( pos ) ) ) {
+ throw new Error("Invalid arguments");
+ }
+
+ shrt = ( shrt === true );
+ bits = ( bits === true );
+ pos = shrt ? 1 : ( pos === undefined ? 2 : parseInt( pos, base ) );
+ num = Number( arg );
+ neg = ( num < 0 );
+
+ // Flipping a negative number to determine the size
+ if ( neg ) {
+ num = -num;
+ }
+
+ // Zero is now a special case because bytes divide by 1
+ if ( num === 0 ) {
+ if ( shrt ) {
+ result = "0";
+ }
+ else {
+ result = "0 B";
+ }
+ }
+ else {
+ if ( bits ) {
+ sizes = options.all.increments;
+ i = options.all.nth;
+ }
+ else {
+ sizes = options.bitless.increments;
+ i = options.bitless.nth;
+ }
+
+ while ( i-- ) {
+ size = sizes[i][1];
+ suffix = sizes[i][0];
+
+ if ( num >= size ) {
+ // Treating bytes as cardinal
+ if ( bite.test( suffix ) ) {
+ skip = true;
+ pos = 0;
+ }
+
+ result = ( num / size ).toFixed( pos );
+
+ if ( !skip && shrt ) {
+ if ( bits && bit.test( suffix ) ) {
+ suffix = suffix.toLowerCase();
+ }
+
+ suffix = suffix.charAt( 0 );
+ z = right.exec( result );
+
+ if ( suffix === "k" ) {
+ suffix = "K";
+ }
+
+ if ( z !== null && z[1] !== undefined && zero.test( z[1] ) ) {
+ result = parseInt( result, base );
+ }
+
+ result += suffix;
+ }
+ else if ( !shrt ) {
+ result += " " + suffix;
+ }
+ break;
+ }
+ }
+ }
+
+ // Decorating a 'diff'
+ if ( neg ) {
+ result = "-" + result;
+ }
+
+ return result;
+ }
+
+ // CommonJS, AMD, script tag
+ if ( typeof exports !== "undefined" ) {
+ module.exports = filesize;
+ }
+ else if ( typeof define === "function" ) {
+ define( function () {
+ return filesize;
+ });
+ }
+ else {
+ global.filesize = filesize;
+ }
+})( this );
diff --git a/UI/JsLibraries/sugar.js b/UI/JsLibraries/sugar.js
deleted file mode 100644
index b6a10f7c4..000000000
--- a/UI/JsLibraries/sugar.js
+++ /dev/null
@@ -1,9015 +0,0 @@
-/*
- * Sugar Library vedge
- *
- * Freely distributable and licensed under the MIT-style license.
- * Copyright (c) 2013 Andrew Plummer
- * http://sugarjs.com/
- *
- * ---------------------------- */
-(function(){
- /***
- * @package Core
- * @description Internal utility and common methods.
- ***/
-
-
- // A few optimizations for Google Closure Compiler will save us a couple kb in the release script.
- var object = Object, array = Array, regexp = RegExp, date = Date, string = String, number = Number, math = Math, Undefined;
-
- // Internal toString
- var internalToString = object.prototype.toString;
-
- // Internal hasOwnProperty
- var internalHasOwnProperty = object.prototype.hasOwnProperty;
-
- // The global context
- var globalContext = typeof global !== 'undefined' ? global : this;
-
- // Type check methods need a way to be accessed dynamically outside global context.
- var typeChecks = {};
-
- // defineProperty exists in IE8 but will error when trying to define a property on
- // native objects. IE8 does not have defineProperies, however, so this check saves a try/catch block.
- var definePropertySupport = object.defineProperty && object.defineProperties;
-
-
- // Class initializers and class helpers
-
- var ClassNames = 'Array,Boolean,Date,Function,Number,String,RegExp'.split(',');
-
- var isArray = buildClassCheck(ClassNames[0]);
- var isBoolean = buildClassCheck(ClassNames[1]);
- var isDate = buildClassCheck(ClassNames[2]);
- var isFunction = buildClassCheck(ClassNames[3]);
- var isNumber = buildClassCheck(ClassNames[4]);
- var isString = buildClassCheck(ClassNames[5]);
- var isRegExp = buildClassCheck(ClassNames[6]);
-
- function buildClassCheck(name) {
- var type, fn;
- if(/String|Number|Boolean/.test(name)) {
- type = name.toLowerCase();
- }
- fn = (name === 'Array' && array.isArray) || function(obj, klass) {
- if(type && typeof obj === type) {
- return true;
- }
- klass = klass || className(obj);
- return klass === '[object '+name+']';
- }
- typeChecks[name] = fn;
- return fn;
- }
-
- function className(obj) {
- return internalToString.call(obj);
- }
-
- function initializeClasses() {
- initializeClass(object);
- iterateOverObject(ClassNames, function(i,name) {
- initializeClass(globalContext[name]);
- });
- }
-
- function initializeClass(klass) {
- if(klass['SugarMethods']) return;
- defineProperty(klass, 'SugarMethods', {});
- extend(klass, false, false, {
- 'extend': function(methods, override, instance) {
- extend(klass, instance !== false, override, methods);
- },
- 'sugarRestore': function() {
- return batchMethodExecute(klass, arguments, function(target, name, m) {
- defineProperty(target, name, m.method);
- });
- },
- 'sugarRevert': function() {
- return batchMethodExecute(klass, arguments, function(target, name, m) {
- if(m.existed) {
- defineProperty(target, name, m.original);
- } else {
- delete target[name];
- }
- });
- }
- });
- }
-
- // Class extending methods
-
- function extend(klass, instance, override, methods) {
- var extendee = instance ? klass.prototype : klass;
- initializeClass(klass);
- iterateOverObject(methods, function(name, method) {
- var original = extendee[name];
- var existed = hasOwnProperty(extendee, name);
- if(typeof override === 'function') {
- method = wrapNative(extendee[name], method, override);
- }
- if(override !== false || !extendee[name]) {
- defineProperty(extendee, name, method);
- }
- // If the method is internal to Sugar, then store a reference so it can be restored later.
- klass['SugarMethods'][name] = { instance: instance, method: method, original: original, existed: existed };
- });
- }
-
- function extendSimilar(klass, instance, override, set, fn) {
- var methods = {};
- set = isString(set) ? set.split(',') : set;
- set.forEach(function(name, i) {
- fn(methods, name, i);
- });
- extend(klass, instance, override, methods);
- }
-
- function batchMethodExecute(klass, args, fn) {
- var all = args.length === 0, methods = multiArgs(args), changed = false;
- iterateOverObject(klass['SugarMethods'], function(name, m) {
- if(all || methods.indexOf(name) !== -1) {
- changed = true;
- fn(m.instance ? klass.prototype : klass, name, m);
- }
- });
- return changed;
- }
-
- function wrapNative(nativeFn, extendedFn, condition) {
- return function() {
- var fn;
- if(nativeFn && (condition === true || !condition.apply(this, arguments))) {
- fn = nativeFn;
- } else {
- fn = extendedFn;
- }
- return fn.apply(this, arguments);
- }
- }
-
- function defineProperty(target, name, method) {
- if(definePropertySupport) {
- object.defineProperty(target, name, { 'value': method, 'configurable': true, 'enumerable': false, 'writable': true });
- } else {
- target[name] = method;
- }
- }
-
-
- // Argument helpers
-
- function multiArgs(args, fn) {
- var result = [], i, len;
- for(i = 0, len = args.length; i < len; i++) {
- result.push(args[i]);
- if(fn) fn.call(args, args[i], i);
- }
- return result;
- }
-
- function flattenedArgs(obj, fn, from) {
- multiArgs(array.prototype.concat.apply([], array.prototype.slice.call(obj, from || 0)), fn);
- }
-
- function checkCallback(fn) {
- if(!fn || !fn.call) {
- throw new TypeError('Callback is not callable');
- }
- }
-
-
- // General helpers
-
- function isDefined(o) {
- return o !== Undefined;
- }
-
- function isUndefined(o) {
- return o === Undefined;
- }
-
-
- // Object helpers
-
- function hasOwnProperty(obj, prop) {
- return !!obj && internalHasOwnProperty.call(obj, prop);
- }
-
- function isObjectPrimitive(obj) {
- // Check for null
- return !!obj && typeof obj === 'object';
- }
-
- function isPlainObject(obj, klass) {
- klass = klass || className(obj);
- try {
- // Not own constructor property must be Object
- // This code was borrowed from jQuery.isPlainObject
- if (obj.constructor &&
- !hasOwnProperty(obj, 'constructor') &&
- !hasOwnProperty(obj.constructor.prototype, 'isPrototypeOf')) {
- return false;
- }
- } catch (e) {
- // IE8,9 Will throw exceptions on certain host objects.
- return false;
- }
- // === on the constructor is not safe across iframes
- // 'hasOwnProperty' ensures that the object also inherits
- // from Object, which is false for DOMElements in IE.
- return !!obj && klass === '[object Object]' && 'hasOwnProperty' in obj;
- }
-
- function iterateOverObject(obj, fn) {
- var key;
- for(key in obj) {
- if(!hasOwnProperty(obj, key)) continue;
- if(fn.call(obj, key, obj[key], obj) === false) break;
- }
- }
-
- function simpleRepeat(n, fn) {
- for(var i = 0; i < n; i++) {
- fn(i);
- }
- }
-
- function simpleMerge(target, source) {
- iterateOverObject(source, function(key) {
- target[key] = source[key];
- });
- return target;
- }
-
- // Hash definition
-
- function Hash(obj) {
- simpleMerge(this, obj);
- };
-
- Hash.prototype.constructor = object;
-
- // Number helpers
-
- function round(val, precision, method) {
- var fn = math[method || 'round'];
- var multiplier = math.pow(10, math.abs(precision || 0));
- if(precision < 0) multiplier = 1 / multiplier;
- return fn(val * multiplier) / multiplier;
- }
-
- function ceil(val, precision) {
- return round(val, precision, 'ceil');
- }
-
- function floor(val, precision) {
- return round(val, precision, 'floor');
- }
-
- // Used by Number and Date
-
- function padNumber(num, place, sign, base) {
- var str = math.abs(num).toString(base || 10);
- str = repeatString(place - str.replace(/\.\d+/, '').length, '0') + str;
- if(sign || num < 0) {
- str = (num < 0 ? '-' : '+') + str;
- }
- return str;
- }
-
- function getOrdinalizedSuffix(num) {
- if(num >= 11 && num <= 13) {
- return 'th';
- } else {
- switch(num % 10) {
- case 1: return 'st';
- case 2: return 'nd';
- case 3: return 'rd';
- default: return 'th';
- }
- }
- }
-
-
- // String helpers
-
- // WhiteSpace/LineTerminator as defined in ES5.1 plus Unicode characters in the Space, Separator category.
- function getTrimmableCharacters() {
- 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';
- }
-
- function repeatString(times, str) {
- var result = '';
- if(isUndefined(times)) {
- times = 1;
- }
- while(times-- > 0) {
- result += str;
- }
- return result;
- }
-
-
- // RegExp helpers
-
- function getRegExpFlags(reg, add) {
- var flags = reg.toString().match(/[^/]*$/)[0];
- if(add) {
- flags = (flags + add).split('').sort().join('').replace(/([gimy])\1+/g, '$1');
- }
- return flags;
- }
-
- function escapeRegExp(str) {
- if(!isString(str)) str = string(str);
- return str.replace(/([\\/\'*+?|()\[\]{}.^$])/g,'\\$1');
- }
-
-
- // Date helpers
-
- function callDateGet(d, method) {
- return d['get' + (d._utc ? 'UTC' : '') + method]();
- }
-
- function callDateSet(d, method, value) {
- return d['set' + (d._utc && method != 'ISOWeek' ? 'UTC' : '') + method](value);
- }
-
- function extractDurationFromString(str) {
- var match, val, unit;
- match = str.toLowerCase().match(/^(\d+)?\s?(\w+?)s?$/i);
- val = parseInt(match[1]) || 1;
- unit = match[2].slice(0,1).toUpperCase() + match[2].slice(1);
- if(unit.match(/hour|minute|second/i)) {
- unit += 's';
- } else if(unit === 'Year') {
- unit = 'FullYear';
- } else if(unit === 'Day') {
- unit = 'Date';
- }
- return [val, unit];
- }
-
- // Specialized helpers
-
-
- // Used by Array#unique and Object.equal
-
- function stringify(thing, stack) {
- var type = typeof thing,
- thingIsObject,
- thingIsArray,
- klass, value,
- arr, key, i, len;
-
- // Return quickly if string to save cycles
- if(type === 'string') return thing;
-
- klass = internalToString.call(thing)
- thingIsObject = isPlainObject(thing, klass);
- thingIsArray = isArray(thing, klass);
-
- if(thing != null && thingIsObject || thingIsArray) {
- // This method for checking for cyclic structures was egregiously stolen from
- // the ingenious method by @kitcambridge from the Underscore script:
- // https://github.com/documentcloud/underscore/issues/240
- if(!stack) stack = [];
- // Allowing a step into the structure before triggering this
- // script to save cycles on standard JSON structures and also to
- // try as hard as possible to catch basic properties that may have
- // been modified.
- if(stack.length > 1) {
- i = stack.length;
- while (i--) {
- if (stack[i] === thing) {
- return 'CYC';
- }
- }
- }
- stack.push(thing);
- value = thing.valueOf() + string(thing.constructor);
- arr = thingIsArray ? thing : object.keys(thing).sort();
- for(i = 0, len = arr.length; i < len; i++) {
- key = thingIsArray ? i : arr[i];
- value += key + stringify(thing[key], stack);
- }
- stack.pop();
- } else if(1 / thing === -Infinity) {
- value = '-0';
- } else {
- value = string(thing && thing.valueOf ? thing.valueOf() : thing);
- }
- return type + klass + value;
- }
-
- function isEqual(a, b) {
- if(objectIsMatchedByValue(a) && objectIsMatchedByValue(b)) {
- return stringify(a) === stringify(b);
- } else {
- return a === b;
- }
- }
-
- function objectIsMatchedByValue(obj) {
- // Only known objects are matched by value. This is notably excluding functions, DOM Elements, and instances of
- // user-created classes. The latter can arguably be matched by value, but distinguishing between these and
- // host objects -- which should never be compared by value -- is very tricky so not dealing with it here.
- var klass = className(obj);
- return /^\[object Date|Array|String|Number|RegExp|Boolean|Arguments\]$/.test(klass) || isPlainObject(obj, klass);
- }
-
-
- // Used by Array#at and String#at
-
- function entryAtIndex(arr, args, str) {
- var result = [], length = arr.length, loop = args[args.length - 1] !== false, r;
- multiArgs(args, function(index) {
- if(isBoolean(index)) return false;
- if(loop) {
- index = index % length;
- if(index < 0) index = length + index;
- }
- r = str ? arr.charAt(index) || '' : arr[index];
- result.push(r);
- });
- return result.length < 2 ? result[0] : result;
- }
-
-
- // Object class methods implemented as instance methods
-
- function buildObjectInstanceMethods(set, target) {
- extendSimilar(target, true, false, set, function(methods, name) {
- methods[name + (name === 'equal' ? 's' : '')] = function() {
- return object[name].apply(null, [this].concat(multiArgs(arguments)));
- }
- });
- }
-
- initializeClasses();
-
-
-
- /***
- * @package ES5
- * @description Shim methods that provide ES5 compatible functionality. This package can be excluded if you do not require legacy browser support (IE8 and below).
- *
- ***/
-
-
- /***
- * Object module
- *
- ***/
-
- extend(object, false, false, {
-
- 'keys': function(obj) {
- var keys = [];
- if(!isObjectPrimitive(obj) && !isRegExp(obj) && !isFunction(obj)) {
- throw new TypeError('Object required');
- }
- iterateOverObject(obj, function(key, value) {
- keys.push(key);
- });
- return keys;
- }
-
- });
-
-
- /***
- * Array module
- *
- ***/
-
- // ECMA5 methods
-
- function arrayIndexOf(arr, search, fromIndex, increment) {
- var length = arr.length,
- fromRight = increment == -1,
- start = fromRight ? length - 1 : 0,
- index = toIntegerWithDefault(fromIndex, start);
- if(index < 0) {
- index = length + index;
- }
- if((!fromRight && index < 0) || (fromRight && index >= length)) {
- index = start;
- }
- while((fromRight && index >= 0) || (!fromRight && index < length)) {
- if(arr[index] === search) {
- return index;
- }
- index += increment;
- }
- return -1;
- }
-
- function arrayReduce(arr, fn, initialValue, fromRight) {
- var length = arr.length, count = 0, defined = isDefined(initialValue), result, index;
- checkCallback(fn);
- if(length == 0 && !defined) {
- throw new TypeError('Reduce called on empty array with no initial value');
- } else if(defined) {
- result = initialValue;
- } else {
- result = arr[fromRight ? length - 1 : count];
- count++;
- }
- while(count < length) {
- index = fromRight ? length - count - 1 : count;
- if(index in arr) {
- result = fn(result, arr[index], index, arr);
- }
- count++;
- }
- return result;
- }
-
- function toIntegerWithDefault(i, d) {
- if(isNaN(i)) {
- return d;
- } else {
- return parseInt(i >> 0);
- }
- }
-
- function checkFirstArgumentExists(args) {
- if(args.length === 0) {
- throw new TypeError('First argument must be defined');
- }
- }
-
-
-
-
- extend(array, false, false, {
-
- /***
- *
- * @method Array.isArray()
- * @returns Boolean
- * @short Returns true if is an Array.
- * @extra This method is provided for browsers that don't support it internally.
- * @example
- *
- * Array.isArray(3) -> false
- * Array.isArray(true) -> false
- * Array.isArray('wasabi') -> false
- * Array.isArray([1,2,3]) -> true
- *
- ***/
- 'isArray': function(obj) {
- return isArray(obj);
- }
-
- });
-
-
- extend(array, true, false, {
-
- /***
- * @method every(, [scope])
- * @returns Boolean
- * @short Returns true if all elements in the array match .
- * @extra [scope] is the %this% object. %all% is provided an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
- * @example
- *
- + ['a','a','a'].every(function(n) {
- * return n == 'a';
- * });
- * ['a','a','a'].every('a') -> true
- * [{a:2},{a:2}].every({a:2}) -> true
- ***/
- 'every': function(fn, scope) {
- var length = this.length, index = 0;
- checkFirstArgumentExists(arguments);
- while(index < length) {
- if(index in this && !fn.call(scope, this[index], index, this)) {
- return false;
- }
- index++;
- }
- return true;
- },
-
- /***
- * @method some(, [scope])
- * @returns Boolean
- * @short Returns true if any element in the array matches .
- * @extra [scope] is the %this% object. %any% is provided as an alias. In addition to providing this method for browsers that don't support it natively, this method also implements @array_matching.
- * @example
- *
- + ['a','b','c'].some(function(n) {
- * return n == 'a';
- * });
- + ['a','b','c'].some(function(n) {
- * return n == 'd';
- * });
- * ['a','b','c'].some('a') -> true
- * [{a:2},{b:5}].some({a:2}) -> true
- ***/
- 'some': function(fn, scope) {
- var length = this.length, index = 0;
- checkFirstArgumentExists(arguments);
- while(index < length) {
- if(index in this && fn.call(scope, this[index], index, this)) {
- return true;
- }
- index++;
- }
- return false;
- },
-
- /***
- * @method map(