diff --git a/dss/settings.py b/dss/settings.py
index c262d41..f3111f2 100644
--- a/dss/settings.py
+++ b/dss/settings.py
@@ -228,6 +228,8 @@ PIPELINE_CSS = {
}
INTERNAL_IPS = ('127.0.0.1', '86.44.166.21')
GOOGLE_ANALYTICS_CODE = localsettings.GOOGLE_ANALYTICS_CODE
+#TASTYPIE_DATETIME_FORMATTING = 'iso-8601'
+TASTYPIE_DATETIME_FORMATTING = 'rfc-2822'
SENDFILE_BACKEND = localsettings.SENDFILE_BACKEND
SENDFILE_ROOT = os.path.join(MEDIA_ROOT, 'mixes')
diff --git a/spa/api/v1/ActivityResource.py b/spa/api/v1/ActivityResource.py
index c82d329..949908f 100644
--- a/spa/api/v1/ActivityResource.py
+++ b/spa/api/v1/ActivityResource.py
@@ -5,9 +5,9 @@ from spa.models._Activity import _Activity
class ActivityResource(BackboneCompatibleResource):
-
class Meta:
- queryset = _Activity.objects.all()
+ #queryset = _Activity.objects.filter(pk=3442).order_by('-date')
+ queryset = _Activity.objects.all().order_by('-date')
resource_name = 'activity'
authorization = Authorization()
authentication = Authentication()
@@ -18,13 +18,14 @@ class ActivityResource(BackboneCompatibleResource):
def dehydrate(self, bundle):
try:
- if bundle.obj.user is not None:
- bundle.data["message"] = "%s %s a %s on %s" %\
- (bundle.obj.user.get_full_name(),
- bundle.obj.get_verb_passed(),
- bundle.obj.get_object_singular(),
- bundle.obj.date)
- return bundle
+ bundle.data["verb"] = bundle.obj.get_verb_passed(),
+ bundle.data["object"] = bundle.obj.get_object_singular(),
+ bundle.data["item_name"] = bundle.obj.get_object_name(),
+ bundle.data["item_url"] = bundle.obj.get_object_url(),
+ bundle.data["user_name"] = bundle.obj.user.get_full_name(),
+ bundle.data["user_profile"] = bundle.obj.user.get_profile().get_profile_url(),
+ bundle.data["user_image"] = bundle.obj.user.get_profile().get_small_profile_image()
+ return bundle
except AttributeError, ae:
self.logger.debug("AttributeError: Error dehydrating activity, %s" % ae.message)
@@ -32,3 +33,10 @@ class ActivityResource(BackboneCompatibleResource):
self.logger.debug("TypeError: Error dehydrating activity, %s" % te.message)
except Exception, ee:
self.logger.debug("Exception: Error dehydrating activity, %s" % ee.message)
+ return None
+
+ def alter_list_data_to_serialize(self, request, data):
+ return [i for i in data['objects'] if i is not None and i.obj.user is not None and i.obj.get_object_name is not None and i.obj.get_object_url is not None]
+
+ def dehydrate_date(self, bundle):
+ return self.humanize_date(bundle.obj.date)
diff --git a/spa/api/v1/BackboneCompatibleResource.py b/spa/api/v1/BackboneCompatibleResource.py
index d7c2053..29680eb 100644
--- a/spa/api/v1/BackboneCompatibleResource.py
+++ b/spa/api/v1/BackboneCompatibleResource.py
@@ -1,12 +1,15 @@
import logging
-from django.conf.urls import url
-from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
-from tastypie import fields
-from tastypie.http import HttpGone, HttpMultipleChoices
+import datetime
+import humanize
from tastypie.resources import ModelResource
-from tastypie.utils import trailing_slash
class BackboneCompatibleResource(ModelResource):
logger = logging.getLogger(__name__)
pass
+
+ def humanize_date(self, date):
+ if (datetime.datetime.now() - date) <= datetime.timedelta(days=1):
+ return humanize.naturaltime(date)
+ else:
+ return humanize.naturalday(date)
\ No newline at end of file
diff --git a/spa/api/v1/CommentResource.py b/spa/api/v1/CommentResource.py
index f493425..198b7a7 100644
--- a/spa/api/v1/CommentResource.py
+++ b/spa/api/v1/CommentResource.py
@@ -1,13 +1,11 @@
-import datetime
-import humanize
from tastypie import fields
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
from spa.models.Comment import Comment
-from tastypie.resources import ModelResource
-class CommentResource(ModelResource):
+
+class CommentResource(BackboneCompatibleResource):
mix = fields.ToOneField('spa.api.v1.MixResource.MixResource', 'mix')
class Meta:
@@ -25,10 +23,7 @@ class CommentResource(ModelResource):
return super(CommentResource, self).obj_create(bundle, request, user=request.user)
def dehydrate_date_created(self, bundle):
- if (datetime.datetime.now() - bundle.obj.date_created) <= datetime.timedelta(days=1):
- return humanize.naturaltime(bundle.obj.date_created)
- else:
- return humanize.naturalday(bundle.obj.date_created)
+ return self.humanize_date(bundle.obj.date_created)
def dehydrate(self, bundle):
bundle.data['avatar_image'] = bundle.obj.user.get_profile().get_small_profile_image()
diff --git a/spa/api/v1/MixResource.py b/spa/api/v1/MixResource.py
index 8057fdd..a322242 100644
--- a/spa/api/v1/MixResource.py
+++ b/spa/api/v1/MixResource.py
@@ -17,7 +17,7 @@ from spa.models.Mix import Mix
class MixResource(BackboneCompatibleResource):
comments = fields.ToManyField('spa.api.v1.CommentResource.CommentResource', 'comments')
- #activity = fields.ToManyField('spa.api.v1.ActivityResource.ActivityResource', 'activity', 'mix', null=True)
+ #downloads = fields.ToManyField('spa.api.v1.ActivityResource.ActivityResource', 'downloads')
class Meta:
queryset = Mix.objects.filter(is_active=True)
diff --git a/spa/models/MixPlay.py b/spa/models/MixPlay.py
index 889f880..906d287 100644
--- a/spa/models/MixPlay.py
+++ b/spa/models/MixPlay.py
@@ -9,3 +9,9 @@ class MixPlay(_Activity):
def get_object_singular(self):
return "mix"
+
+ def get_object_name(self):
+ return self.mix.title
+
+ def get_object_url(self):
+ return self.mix.get_absolute_url()
\ No newline at end of file
diff --git a/spa/models/_Activity.py b/spa/models/_Activity.py
index 1a86355..3e694d4 100644
--- a/spa/models/_Activity.py
+++ b/spa/models/_Activity.py
@@ -26,3 +26,11 @@ class _Activity(_BaseModel):
@abc.abstractmethod
def get_object_plural(self):
return
+
+ @abc.abstractmethod
+ def get_object_name(self):
+ return
+
+ @abc.abstractmethod
+ def get_object_url(self):
+ return
diff --git a/static/css/deepsouthsounds.css b/static/css/deepsouthsounds.css
index 10fd282..13ce065 100644
--- a/static/css/deepsouthsounds.css
+++ b/static/css/deepsouthsounds.css
@@ -38,6 +38,9 @@ img.event-content {
.bordered {
border-bottom: 3px solid #CEC3B3;
}
+.bordered-faint {
+ border-bottom: 1px solid #CEC3B3;
+}
.page-header {
-moz-border-bottom-colors: none;
@@ -583,4 +586,9 @@ div.event-content td {
.mix-profile-insert {
+}
+
+.activity-list-item{
+ margin-bottom: 5px;
+ margin-left: 5px;
}
\ No newline at end of file
diff --git a/static/js/app/views/sidebar.js b/static/js/app/views/sidebar.js
index 67ef98c..01acd11 100644
--- a/static/js/app/views/sidebar.js
+++ b/static/js/app/views/sidebar.js
@@ -34,7 +34,7 @@ window.SidebarView = Backbone.View.extend({
var content = new ActivityListView({
collection: activity
}).el;
- $('.sidebar-content-activity', this.el).html(content.el);
+ $('#sidebar-content-activity', this.el).html(content);
}
});
return this;
diff --git a/static/js/com.podnoms.utils.js b/static/js/com.podnoms.utils.js
index c2bc436..f4b83cc 100644
--- a/static/js/com.podnoms.utils.js
+++ b/static/js/com.podnoms.utils.js
@@ -12,7 +12,7 @@ if (!com.podnoms) com.podnoms = {};
com.podnoms.utils = {
// Asynchronously load templates located in separate .html files
- loadTemplate:function (views, callback) {
+ loadTemplate: function (views, callback) {
var deferreds = [];
$.each(views, function (index, view) {
if (window[view]) {
@@ -25,11 +25,11 @@ com.podnoms.utils = {
});
$.when.apply(null, deferreds).done(callback);
},
- trackPageView:function (url) {
+ trackPageView: function (url) {
if (!(typeof(_gag) == "undefined"))
_gaq.push(['_trackPageview', "/" + url]);
},
- displayValidationErrors:function (messages) {
+ displayValidationErrors: function (messages) {
for (var key in messages) {
if (messages.hasOwnProperty(key)) {
this.addValidationError(key, messages[key]);
@@ -37,20 +37,20 @@ com.podnoms.utils = {
}
this.showAlert('Warning!', 'Fix validation errors and try again', 'alert-warning');
},
- addValidationError:function (field, message) {
+ addValidationError: function (field, message) {
var controlGroup = $('#' + field).parent().parent();
controlGroup.addClass('error');
$('.help-inline', controlGroup).html(message);
},
- removeValidationError:function (field) {
+ removeValidationError: function (field) {
var controlGroup = $('#' + field).parent().parent();
controlGroup.removeClass('error');
$('.help-inline', controlGroup).html('');
},
- showError:function (title, message) {
+ showError: function (title, message) {
this.showAlert(title, message, 'alert-error', true);
},
- showAlert:function (title, text, klass, fade) {
+ showAlert: function (title, text, klass, fade) {
$('.alert').removeClass("alert-error alert-warning alert-success alert-info");
$('.alert').addClass(klass);
$('.alert').html('' + title + ' ' + text);
@@ -63,33 +63,37 @@ com.podnoms.utils = {
this.hideAlert();
});
},
- hideAlert:function () {
+ hideAlert: function () {
$('.alert').fadeOut('slow', function () {
});
},
- pad2:function (number) {
+ pad2: function (number) {
return (number < 10 ? '0' : '') + number;
},
- getDateAsToday:function () {
+ getDateAsToday: function () {
var currentTime = new Date();
var day = currentTime.getDate();
var month = currentTime.getMonth() + 1;
var year = currentTime.getFullYear();
return (com.podnoms.utils.pad2(day) + "/" + com.podnoms.utils.pad2(month) + "/" + year);
},
- isEmpty:function (val) {
+ formatJSONDate: function (jsonDate) {
+ var date = new Date(parseInt(jsonDate.substr(6)));
+ return date;
+ },
+ isEmpty: function (val) {
return (val === undefined || val == null || val.length <= 0) ? true : false;
},
- setHashbangHeader:function (xhr) {
+ setHashbangHeader: function (xhr) {
xhr.setRequestHeader('X-FB-Nonsense', 'Argle-Bargle');
},
- generateGuid:function () {
+ generateGuid: function () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
},
- downloadURL:function downloadURL(url) {
+ downloadURL: function downloadURL(url) {
var iframe;
iframe = document.getElementById("hiddenDownloader");
if (iframe === null) {
@@ -103,7 +107,7 @@ com.podnoms.utils = {
};
jQuery.extend({
- handleError:function (s, xhr, status, e) {
+ handleError: function (s, xhr, status, e) {
// If a local callback was specified, fire it
if (s.error) {
s.error.call(s.context || window, xhr, status, e);
diff --git a/static/js/libs/globalize.js b/static/js/libs/globalize.js
new file mode 100644
index 0000000..48a9edd
--- /dev/null
+++ b/static/js/libs/globalize.js
@@ -0,0 +1,1609 @@
+/*!
+ * Globalize
+ *
+ * http://github.com/jquery/globalize
+ *
+ * Copyright Software Freedom Conservancy, Inc.
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ */
+
+(function (window, undefined) {
+
+ var Globalize,
+ // private variables
+ regexHex,
+ regexInfinity,
+ regexParseFloat,
+ regexTrim,
+ // private JavaScript utility functions
+ arrayIndexOf,
+ endsWith,
+ extend,
+ isArray,
+ isFunction,
+ isObject,
+ startsWith,
+ trim,
+ truncate,
+ zeroPad,
+ // private Globalization utility functions
+ appendPreOrPostMatch,
+ expandFormat,
+ formatDate,
+ formatNumber,
+ getTokenRegExp,
+ getEra,
+ getEraYear,
+ parseExact,
+ parseNegativePattern;
+
+// Global variable (Globalize) or CommonJS module (globalize)
+ Globalize = function (cultureSelector) {
+ return new Globalize.prototype.init(cultureSelector);
+ };
+
+ if (typeof require !== "undefined" &&
+ typeof exports !== "undefined" &&
+ typeof module !== "undefined") {
+ // Assume CommonJS
+ module.exports = Globalize;
+ } else {
+ // Export as global variable
+ window.Globalize = Globalize;
+ }
+
+ Globalize.cultures = {};
+
+ Globalize.prototype = {
+ constructor: Globalize,
+ init: function (cultureSelector) {
+ this.cultures = Globalize.cultures;
+ this.cultureSelector = cultureSelector;
+
+ return this;
+ }
+ };
+ Globalize.prototype.init.prototype = Globalize.prototype;
+
+// 1. When defining a culture, all fields are required except the ones stated as optional.
+// 2. Each culture should have a ".calendars" object with at least one calendar named "standard"
+// which serves as the default calendar in use by that culture.
+// 3. Each culture should have a ".calendar" object which is the current calendar being used,
+// it may be dynamically changed at any time to one of the calendars in ".calendars".
+ Globalize.cultures[ "default" ] = {
+ // A unique name for the culture in the form -
+ name: "en",
+ // the name of the culture in the english language
+ englishName: "English",
+ // the name of the culture in its own language
+ nativeName: "English",
+ // whether the culture uses right-to-left text
+ isRTL: false,
+ // "language" is used for so-called "specific" cultures.
+ // For example, the culture "es-CL" means "Spanish, in Chili".
+ // It represents the Spanish-speaking culture as it is in Chili,
+ // which might have different formatting rules or even translations
+ // than Spanish in Spain. A "neutral" culture is one that is not
+ // specific to a region. For example, the culture "es" is the generic
+ // Spanish culture, which may be a more generalized version of the language
+ // that may or may not be what a specific culture expects.
+ // For a specific culture like "es-CL", the "language" field refers to the
+ // neutral, generic culture information for the language it is using.
+ // This is not always a simple matter of the string before the dash.
+ // For example, the "zh-Hans" culture is netural (Simplified Chinese).
+ // And the "zh-SG" culture is Simplified Chinese in Singapore, whose lanugage
+ // field is "zh-CHS", not "zh".
+ // This field should be used to navigate from a specific culture to it's
+ // more general, neutral culture. If a culture is already as general as it
+ // can get, the language may refer to itself.
+ language: "en",
+ // numberFormat defines general number formatting rules, like the digits in
+ // each grouping, the group separator, and how negative numbers are displayed.
+ numberFormat: {
+ // [negativePattern]
+ // Note, numberFormat.pattern has no "positivePattern" unlike percent and currency,
+ // but is still defined as an array for consistency with them.
+ // negativePattern: one of "(n)|-n|- n|n-|n -"
+ pattern: [ "-n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // symbol used for positive numbers
+ "+": "+",
+ // symbol used for negative numbers
+ "-": "-",
+ // symbol used for NaN (Not-A-Number)
+ "NaN": "NaN",
+ // symbol used for Negative Infinity
+ negativeInfinity: "-Infinity",
+ // symbol used for Positive Infinity
+ positiveInfinity: "Infinity",
+ percent: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "-n %|-n%|-%n|%-n|%n-|n-%|n%-|-% n|n %-|% n-|% -n|n- %"
+ // positivePattern: one of "n %|n%|%n|% n"
+ pattern: [ "-n %", "n %" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent a percentage
+ symbol: "%"
+ },
+ currency: {
+ // [negativePattern, positivePattern]
+ // negativePattern: one of "($n)|-$n|$-n|$n-|(n$)|-n$|n-$|n$-|-n $|-$ n|n $-|$ n-|$ -n|n- $|($ n)|(n $)"
+ // positivePattern: one of "$n|n$|$ n|n $"
+ pattern: [ "($n)", "$n" ],
+ // number of decimal places normally shown
+ decimals: 2,
+ // array of numbers indicating the size of each number group.
+ // TODO: more detailed description and example
+ groupSizes: [ 3 ],
+ // string that separates number groups, as in 1,000,000
+ ",": ",",
+ // string that separates a number from the fractional portion, as in 1.99
+ ".": ".",
+ // symbol used to represent currency
+ symbol: "$"
+ }
+ },
+ // calendars defines all the possible calendars used by this culture.
+ // There should be at least one defined with name "standard", and is the default
+ // calendar used by the culture.
+ // A calendar contains information about how dates are formatted, information about
+ // the calendar's eras, a standard set of the date formats,
+ // translations for day and month names, and if the calendar is not based on the Gregorian
+ // calendar, conversion functions to and from the Gregorian calendar.
+ calendars: {
+ standard: {
+ // name that identifies the type of calendar this is
+ name: "Gregorian_USEnglish",
+ // separator of parts of a date (e.g. "/" in 11/05/1955)
+ "/": "/",
+ // separator of parts of a time (e.g. ":" in 05:44 PM)
+ ":": ":",
+ // the first day of the week (0 = Sunday, 1 = Monday, etc)
+ firstDay: 0,
+ days: {
+ // full day names
+ names: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
+ // abbreviated day names
+ namesAbbr: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
+ // shortest day names
+ namesShort: [ "Su", "Mo", "Tu", "We", "Th", "Fr", "Sa" ]
+ },
+ months: {
+ // full month names (13 months for lunar calendards -- 13th month should be "" if not lunar)
+ names: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December", "" ],
+ // abbreviated month names
+ namesAbbr: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "" ]
+ },
+ // AM and PM designators in one of these forms:
+ // The usual view, and the upper and lower case versions
+ // [ standard, lowercase, uppercase ]
+ // The culture does not use AM or PM (likely all standard date formats use 24 hour time)
+ // null
+ AM: [ "AM", "am", "AM" ],
+ PM: [ "PM", "pm", "PM" ],
+ eras: [
+ // eras in reverse chronological order.
+ // name: the name of the era in this culture (e.g. A.D., C.E.)
+ // start: when the era starts in ticks (gregorian, gmt), null if it is the earliest supported era.
+ // offset: offset in years from gregorian calendar
+ {
+ "name": "A.D.",
+ "start": null,
+ "offset": 0
+ }
+ ],
+ // when a two digit year is given, it will never be parsed as a four digit
+ // year greater than this year (in the appropriate era for the culture)
+ // Set it as a full year (e.g. 2029) or use an offset format starting from
+ // the current year: "+19" would correspond to 2029 if the current year 2010.
+ twoDigitYearMax: 2029,
+ // set of predefined date and time patterns used by the culture
+ // these represent the format someone in this culture would expect
+ // to see given the portions of the date that are shown.
+ patterns: {
+ // short date pattern
+ d: "M/d/yyyy",
+ // long date pattern
+ D: "dddd, MMMM dd, yyyy",
+ // short time pattern
+ t: "h:mm tt",
+ // long time pattern
+ T: "h:mm:ss tt",
+ // long date, short time pattern
+ f: "dddd, MMMM dd, yyyy h:mm tt",
+ // long date, long time pattern
+ F: "dddd, MMMM dd, yyyy h:mm:ss tt",
+ // month/day pattern
+ M: "MMMM dd",
+ // month/year pattern
+ Y: "yyyy MMMM",
+ // S is a sortable format that does not vary by culture
+ S: "yyyy\u0027-\u0027MM\u0027-\u0027dd\u0027T\u0027HH\u0027:\u0027mm\u0027:\u0027ss"
+ }
+ // optional fields for each calendar:
+ /*
+ monthsGenitive:
+ Same as months but used when the day preceeds the month.
+ Omit if the culture has no genitive distinction in month names.
+ For an explaination of genitive months, see http://blogs.msdn.com/michkap/archive/2004/12/25/332259.aspx
+ convert:
+ Allows for the support of non-gregorian based calendars. This convert object is used to
+ to convert a date to and from a gregorian calendar date to handle parsing and formatting.
+ The two functions:
+ fromGregorian( date )
+ Given the date as a parameter, return an array with parts [ year, month, day ]
+ corresponding to the non-gregorian based year, month, and day for the calendar.
+ toGregorian( year, month, day )
+ Given the non-gregorian year, month, and day, return a new Date() object
+ set to the corresponding date in the gregorian calendar.
+ */
+ }
+ },
+ // For localized strings
+ messages: {}
+ };
+
+ Globalize.cultures[ "default" ].calendar = Globalize.cultures[ "default" ].calendars.standard;
+
+ Globalize.cultures.en = Globalize.cultures[ "default" ];
+
+ Globalize.cultureSelector = "en";
+
+//
+// private variables
+//
+
+ regexHex = /^0x[a-f0-9]+$/i;
+ regexInfinity = /^[+\-]?infinity$/i;
+ regexParseFloat = /^[+\-]?\d*\.?\d*(e[+\-]?\d+)?$/;
+ regexTrim = /^\s+|\s+$/g;
+
+//
+// private JavaScript utility functions
+//
+
+ arrayIndexOf = function (array, item) {
+ if (array.indexOf) {
+ return array.indexOf(item);
+ }
+ for (var i = 0, length = array.length; i < length; i++) {
+ if (array[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ };
+
+ endsWith = function (value, pattern) {
+ return value.substr(value.length - pattern.length) === pattern;
+ };
+
+ extend = function () {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if (typeof target === "boolean") {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if (typeof target !== "object" && !isFunction(target)) {
+ target = {};
+ }
+
+ for (; i < length; i++) {
+ // Only deal with non-null/undefined values
+ if ((options = arguments[ i ]) != null) {
+ // Extend the base object
+ for (name in options) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if (target === copy) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if (deep && copy && ( isObject(copy) || (copyIsArray = isArray(copy)) )) {
+ if (copyIsArray) {
+ copyIsArray = false;
+ clone = src && isArray(src) ? src : [];
+
+ } else {
+ clone = src && isObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = extend(deep, clone, copy);
+
+ // Don't bring in undefined values
+ } else if (copy !== undefined) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+ };
+
+ isArray = Array.isArray || function (obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ };
+
+ isFunction = function (obj) {
+ return Object.prototype.toString.call(obj) === "[object Function]";
+ };
+
+ isObject = function (obj) {
+ return Object.prototype.toString.call(obj) === "[object Object]";
+ };
+
+ startsWith = function (value, pattern) {
+ return value.indexOf(pattern) === 0;
+ };
+
+ trim = function (value) {
+ return ( value + "" ).replace(regexTrim, "");
+ };
+
+ truncate = function (value) {
+ if (isNaN(value)) {
+ return NaN;
+ }
+ return Math[ value < 0 ? "ceil" : "floor" ](value);
+ };
+
+ zeroPad = function (str, count, left) {
+ var l;
+ for (l = str.length; l < count; l += 1) {
+ str = ( left ? ("0" + str) : (str + "0") );
+ }
+ return str;
+ };
+
+//
+// private Globalization utility functions
+//
+
+ appendPreOrPostMatch = function (preMatch, strings) {
+ // appends pre- and post- token match strings while removing escaped characters.
+ // Returns a single quote count which is used to determine if the token occurs
+ // in a string literal.
+ var quoteCount = 0,
+ escaped = false;
+ for (var i = 0, il = preMatch.length; i < il; i++) {
+ var c = preMatch.charAt(i);
+ switch (c) {
+ case "\'":
+ if (escaped) {
+ strings.push("\'");
+ }
+ else {
+ quoteCount++;
+ }
+ escaped = false;
+ break;
+ case "\\":
+ if (escaped) {
+ strings.push("\\");
+ }
+ escaped = !escaped;
+ break;
+ default:
+ strings.push(c);
+ escaped = false;
+ break;
+ }
+ }
+ return quoteCount;
+ };
+
+ expandFormat = function (cal, format) {
+ // expands unspecified or single character date formats into the full pattern.
+ format = format || "F";
+ var pattern,
+ patterns = cal.patterns,
+ len = format.length;
+ if (len === 1) {
+ pattern = patterns[ format ];
+ if (!pattern) {
+ throw "Invalid date format string \'" + format + "\'.";
+ }
+ format = pattern;
+ }
+ else if (len === 2 && format.charAt(0) === "%") {
+ // %X escape format -- intended as a custom format string that is only one character, not a built-in format.
+ format = format.charAt(1);
+ }
+ return format;
+ };
+
+ formatDate = function (value, format, culture) {
+ var cal = culture.calendar,
+ convert = cal.convert,
+ ret;
+
+ if (!format || !format.length || format === "i") {
+ if (culture && culture.name.length) {
+ if (convert) {
+ // non-gregorian calendar, so we cannot use built-in toLocaleString()
+ ret = formatDate(value, cal.patterns.F, culture);
+ }
+ else {
+ var eraDate = new Date(value.getTime()),
+ era = getEra(value, cal.eras);
+ eraDate.setFullYear(getEraYear(value, cal, era));
+ ret = eraDate.toLocaleString();
+ }
+ }
+ else {
+ ret = value.toString();
+ }
+ return ret;
+ }
+
+ var eras = cal.eras,
+ sortable = format === "s";
+ format = expandFormat(cal, format);
+
+ // Start with an empty string
+ ret = [];
+ var hour,
+ zeros = [ "0", "00", "000" ],
+ foundDay,
+ checkedDay,
+ dayPartRegExp = /([^d]|^)(d|dd)([^d]|$)/g,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ converted;
+
+ function padZeros(num, c) {
+ var r, s = num + "";
+ if (c > 1 && s.length < c) {
+ r = ( zeros[c - 2] + s);
+ return r.substr(r.length - c, c);
+ }
+ else {
+ r = s;
+ }
+ return r;
+ }
+
+ function hasDay() {
+ if (foundDay || checkedDay) {
+ return foundDay;
+ }
+ foundDay = dayPartRegExp.test(format);
+ checkedDay = true;
+ return foundDay;
+ }
+
+ function getPart(date, part) {
+ if (converted) {
+ return converted[ part ];
+ }
+ switch (part) {
+ case 0:
+ return date.getFullYear();
+ case 1:
+ return date.getMonth();
+ case 2:
+ return date.getDate();
+ default:
+ throw "Invalid part value " + part;
+ }
+ }
+
+ if (!sortable && convert) {
+ converted = convert.fromGregorian(value);
+ }
+
+ for (; ;) {
+ // Save the current index
+ var index = tokenRegExp.lastIndex,
+ // Look for the next pattern
+ ar = tokenRegExp.exec(format);
+
+ // Append the text before the pattern (or the end of the string if not found)
+ var preMatch = format.slice(index, ar ? ar.index : format.length);
+ quoteCount += appendPreOrPostMatch(preMatch, ret);
+
+ if (!ar) {
+ break;
+ }
+
+ // do not replace any matches that occur inside a string literal.
+ if (quoteCount % 2) {
+ ret.push(ar[0]);
+ continue;
+ }
+
+ var current = ar[ 0 ],
+ clength = current.length;
+
+ switch (current) {
+ case "ddd":
+ //Day of the week, as a three-letter abbreviation
+ case "dddd":
+ // Day of the week, using the full name
+ var names = ( clength === 3 ) ? cal.days.namesAbbr : cal.days.names;
+ ret.push(names[value.getDay()]);
+ break;
+ case "d":
+ // Day of month, without leading zero for single-digit days
+ case "dd":
+ // Day of month, with leading zero for single-digit days
+ foundDay = true;
+ ret.push(
+ padZeros(getPart(value, 2), clength)
+ );
+ break;
+ case "MMM":
+ // Month, as a three-letter abbreviation
+ case "MMMM":
+ // Month, using the full name
+ var part = getPart(value, 1);
+ ret.push(
+ ( cal.monthsGenitive && hasDay() ) ?
+ ( cal.monthsGenitive[ clength === 3 ? "namesAbbr" : "names" ][ part ] ) :
+ ( cal.months[ clength === 3 ? "namesAbbr" : "names" ][ part ] )
+ );
+ break;
+ case "M":
+ // Month, as digits, with no leading zero for single-digit months
+ case "MM":
+ // Month, as digits, with leading zero for single-digit months
+ ret.push(
+ padZeros(getPart(value, 1) + 1, clength)
+ );
+ break;
+ case "y":
+ // Year, as two digits, but with no leading zero for years less than 10
+ case "yy":
+ // Year, as two digits, with leading zero for years less than 10
+ case "yyyy":
+ // Year represented by four full digits
+ part = converted ? converted[ 0 ] : getEraYear(value, cal, getEra(value, eras), sortable);
+ if (clength < 4) {
+ part = part % 100;
+ }
+ ret.push(
+ padZeros(part, clength)
+ );
+ break;
+ case "h":
+ // Hours with no leading zero for single-digit hours, using 12-hour clock
+ case "hh":
+ // Hours with leading zero for single-digit hours, using 12-hour clock
+ hour = value.getHours() % 12;
+ if (hour === 0) hour = 12;
+ ret.push(
+ padZeros(hour, clength)
+ );
+ break;
+ case "H":
+ // Hours with no leading zero for single-digit hours, using 24-hour clock
+ case "HH":
+ // Hours with leading zero for single-digit hours, using 24-hour clock
+ ret.push(
+ padZeros(value.getHours(), clength)
+ );
+ break;
+ case "m":
+ // Minutes with no leading zero for single-digit minutes
+ case "mm":
+ // Minutes with leading zero for single-digit minutes
+ ret.push(
+ padZeros(value.getMinutes(), clength)
+ );
+ break;
+ case "s":
+ // Seconds with no leading zero for single-digit seconds
+ case "ss":
+ // Seconds with leading zero for single-digit seconds
+ ret.push(
+ padZeros(value.getSeconds(), clength)
+ );
+ break;
+ case "t":
+ // One character am/pm indicator ("a" or "p")
+ case "tt":
+ // Multicharacter am/pm indicator
+ part = value.getHours() < 12 ? ( cal.AM ? cal.AM[0] : " " ) : ( cal.PM ? cal.PM[0] : " " );
+ ret.push(clength === 1 ? part.charAt(0) : part);
+ break;
+ case "f":
+ // Deciseconds
+ case "ff":
+ // Centiseconds
+ case "fff":
+ // Milliseconds
+ ret.push(
+ padZeros(value.getMilliseconds(), 3).substr(0, clength)
+ );
+ break;
+ case "z":
+ // Time zone offset, no leading zero
+ case "zz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros(Math.floor(Math.abs(hour)), clength)
+ );
+ break;
+ case "zzz":
+ // Time zone offset with leading zero
+ hour = value.getTimezoneOffset() / 60;
+ ret.push(
+ ( hour <= 0 ? "+" : "-" ) + padZeros(Math.floor(Math.abs(hour)), 2) +
+ // Hard coded ":" separator, rather than using cal.TimeSeparator
+ // Repeated here for consistency, plus ":" was already assumed in date parsing.
+ ":" + padZeros(Math.abs(value.getTimezoneOffset() % 60), 2)
+ );
+ break;
+ case "g":
+ case "gg":
+ if (cal.eras) {
+ ret.push(
+ cal.eras[ getEra(value, eras) ].name
+ );
+ }
+ break;
+ case "/":
+ ret.push(cal["/"]);
+ break;
+ default:
+ throw "Invalid date format pattern \'" + current + "\'.";
+ }
+ }
+ return ret.join("");
+ };
+
+// formatNumber
+ (function () {
+ var expandNumber;
+
+ expandNumber = function (number, precision, formatInfo) {
+ var groupSizes = formatInfo.groupSizes,
+ curSize = groupSizes[ 0 ],
+ curGroupIndex = 1,
+ factor = Math.pow(10, precision),
+ rounded = Math.round(number * factor) / factor;
+
+ if (!isFinite(rounded)) {
+ rounded = number;
+ }
+ number = rounded;
+
+ var numberString = number + "",
+ right = "",
+ split = numberString.split(/e/i),
+ exponent = split.length > 1 ? parseInt(split[1], 10) : 0;
+ numberString = split[ 0 ];
+ split = numberString.split(".");
+ numberString = split[ 0 ];
+ right = split.length > 1 ? split[ 1 ] : "";
+
+ var l;
+ if (exponent > 0) {
+ right = zeroPad(right, exponent, false);
+ numberString += right.slice(0, exponent);
+ right = right.substr(exponent);
+ }
+ else if (exponent < 0) {
+ exponent = -exponent;
+ numberString = zeroPad(numberString, exponent + 1, true);
+ right = numberString.slice(-exponent, numberString.length) + right;
+ numberString = numberString.slice(0, -exponent);
+ }
+
+ if (precision > 0) {
+ right = formatInfo[ "." ] +
+ ( (right.length > precision) ? right.slice(0, precision) : zeroPad(right, precision) );
+ }
+ else {
+ right = "";
+ }
+
+ var stringIndex = numberString.length - 1,
+ sep = formatInfo[ "," ],
+ ret = "";
+
+ while (stringIndex >= 0) {
+ if (curSize === 0 || curSize > stringIndex) {
+ return numberString.slice(0, stringIndex + 1) + ( ret.length ? (sep + ret + right) : right );
+ }
+ ret = numberString.slice(stringIndex - curSize + 1, stringIndex + 1) + ( ret.length ? (sep + ret) : "" );
+
+ stringIndex -= curSize;
+
+ if (curGroupIndex < groupSizes.length) {
+ curSize = groupSizes[ curGroupIndex ];
+ curGroupIndex++;
+ }
+ }
+
+ return numberString.slice(0, stringIndex + 1) + sep + ret + right;
+ };
+
+ formatNumber = function (value, format, culture) {
+ if (!isFinite(value)) {
+ if (value === Infinity) {
+ return culture.numberFormat.positiveInfinity;
+ }
+ if (value === -Infinity) {
+ return culture.numberFormat.negativeInfinity;
+ }
+ return culture.numberFormat.NaN;
+ }
+ if (!format || format === "i") {
+ return culture.name.length ? value.toLocaleString() : value.toString();
+ }
+ format = format || "D";
+
+ var nf = culture.numberFormat,
+ number = Math.abs(value),
+ precision = -1,
+ pattern;
+ if (format.length > 1) precision = parseInt(format.slice(1), 10);
+
+ var current = format.charAt(0).toUpperCase(),
+ formatInfo;
+
+ switch (current) {
+ case "D":
+ pattern = "n";
+ number = truncate(number);
+ if (precision !== -1) {
+ number = zeroPad("" + number, precision, true);
+ }
+ if (value < 0) number = "-" + number;
+ break;
+ case "N":
+ formatInfo = nf;
+ /* falls through */
+ case "C":
+ formatInfo = formatInfo || nf.currency;
+ /* falls through */
+ case "P":
+ formatInfo = formatInfo || nf.percent;
+ pattern = value < 0 ? formatInfo.pattern[ 0 ] : ( formatInfo.pattern[1] || "n" );
+ if (precision === -1) precision = formatInfo.decimals;
+ number = expandNumber(number * (current === "P" ? 100 : 1), precision, formatInfo);
+ break;
+ default:
+ throw "Bad number format specifier: " + current;
+ }
+
+ var patternParts = /n|\$|-|%/g,
+ ret = "";
+ for (; ;) {
+ var index = patternParts.lastIndex,
+ ar = patternParts.exec(pattern);
+
+ ret += pattern.slice(index, ar ? ar.index : pattern.length);
+
+ if (!ar) {
+ break;
+ }
+
+ switch (ar[0]) {
+ case "n":
+ ret += number;
+ break;
+ case "$":
+ ret += nf.currency.symbol;
+ break;
+ case "-":
+ // don't make 0 negative
+ if (/[1-9]/.test(number)) {
+ ret += nf[ "-" ];
+ }
+ break;
+ case "%":
+ ret += nf.percent.symbol;
+ break;
+ }
+ }
+
+ return ret;
+ };
+
+ }());
+
+ getTokenRegExp = function () {
+ // regular expression for matching date and time tokens in format strings.
+ return (/\/|dddd|ddd|dd|d|MMMM|MMM|MM|M|yyyy|yy|y|hh|h|HH|H|mm|m|ss|s|tt|t|fff|ff|f|zzz|zz|z|gg|g/g);
+ };
+
+ getEra = function (date, eras) {
+ if (!eras) return 0;
+ var start, ticks = date.getTime();
+ for (var i = 0, l = eras.length; i < l; i++) {
+ start = eras[ i ].start;
+ if (start === null || ticks >= start) {
+ return i;
+ }
+ }
+ return 0;
+ };
+
+ getEraYear = function (date, cal, era, sortable) {
+ var year = date.getFullYear();
+ if (!sortable && cal.eras) {
+ // convert normal gregorian year to era-shifted gregorian
+ // year by subtracting the era offset
+ year -= cal.eras[ era ].offset;
+ }
+ return year;
+ };
+
+// parseExact
+ (function () {
+ var expandYear,
+ getDayIndex,
+ getMonthIndex,
+ getParseRegExp,
+ outOfRange,
+ toUpper,
+ toUpperArray;
+
+ expandYear = function (cal, year) {
+ // expands 2-digit year into 4 digits.
+ if (year < 100) {
+ var now = new Date(),
+ era = getEra(now),
+ curr = getEraYear(now, cal, era),
+ twoDigitYearMax = cal.twoDigitYearMax;
+ twoDigitYearMax = typeof twoDigitYearMax === "string" ? new Date().getFullYear() % 100 + parseInt(twoDigitYearMax, 10) : twoDigitYearMax;
+ year += curr - ( curr % 100 );
+ if (year > twoDigitYearMax) {
+ year -= 100;
+ }
+ }
+ return year;
+ };
+
+ getDayIndex = function (cal, value, abbr) {
+ var ret,
+ days = cal.days,
+ upperDays = cal._upperDays;
+ if (!upperDays) {
+ cal._upperDays = upperDays = [
+ toUpperArray(days.names),
+ toUpperArray(days.namesAbbr),
+ toUpperArray(days.namesShort)
+ ];
+ }
+ value = toUpper(value);
+ if (abbr) {
+ ret = arrayIndexOf(upperDays[1], value);
+ if (ret === -1) {
+ ret = arrayIndexOf(upperDays[2], value);
+ }
+ }
+ else {
+ ret = arrayIndexOf(upperDays[0], value);
+ }
+ return ret;
+ };
+
+ getMonthIndex = function (cal, value, abbr) {
+ var months = cal.months,
+ monthsGen = cal.monthsGenitive || cal.months,
+ upperMonths = cal._upperMonths,
+ upperMonthsGen = cal._upperMonthsGen;
+ if (!upperMonths) {
+ cal._upperMonths = upperMonths = [
+ toUpperArray(months.names),
+ toUpperArray(months.namesAbbr)
+ ];
+ cal._upperMonthsGen = upperMonthsGen = [
+ toUpperArray(monthsGen.names),
+ toUpperArray(monthsGen.namesAbbr)
+ ];
+ }
+ value = toUpper(value);
+ var i = arrayIndexOf(abbr ? upperMonths[1] : upperMonths[0], value);
+ if (i < 0) {
+ i = arrayIndexOf(abbr ? upperMonthsGen[1] : upperMonthsGen[0], value);
+ }
+ return i;
+ };
+
+ getParseRegExp = function (cal, format) {
+ // converts a format string into a regular expression with groups that
+ // can be used to extract date fields from a date string.
+ // check for a cached parse regex.
+ var re = cal._parseRegExp;
+ if (!re) {
+ cal._parseRegExp = re = {};
+ }
+ else {
+ var reFormat = re[ format ];
+ if (reFormat) {
+ return reFormat;
+ }
+ }
+
+ // expand single digit formats, then escape regular expression characters.
+ var expFormat = expandFormat(cal, format).replace(/([\^\$\.\*\+\?\|\[\]\(\)\{\}])/g, "\\\\$1"),
+ regexp = [ "^" ],
+ groups = [],
+ index = 0,
+ quoteCount = 0,
+ tokenRegExp = getTokenRegExp(),
+ match;
+
+ // iterate through each date token found.
+ while ((match = tokenRegExp.exec(expFormat)) !== null) {
+ var preMatch = expFormat.slice(index, match.index);
+ index = tokenRegExp.lastIndex;
+
+ // don't replace any matches that occur inside a string literal.
+ quoteCount += appendPreOrPostMatch(preMatch, regexp);
+ if (quoteCount % 2) {
+ regexp.push(match[0]);
+ continue;
+ }
+
+ // add a regex group for the token.
+ var m = match[ 0 ],
+ len = m.length,
+ add;
+ switch (m) {
+ case "dddd":
+ case "ddd":
+ case "MMMM":
+ case "MMM":
+ case "gg":
+ case "g":
+ add = "(\\D+)";
+ break;
+ case "tt":
+ case "t":
+ add = "(\\D*)";
+ break;
+ case "yyyy":
+ case "fff":
+ case "ff":
+ case "f":
+ add = "(\\d{" + len + "})";
+ break;
+ case "dd":
+ case "d":
+ case "MM":
+ case "M":
+ case "yy":
+ case "y":
+ case "HH":
+ case "H":
+ case "hh":
+ case "h":
+ case "mm":
+ case "m":
+ case "ss":
+ case "s":
+ add = "(\\d\\d?)";
+ break;
+ case "zzz":
+ add = "([+-]?\\d\\d?:\\d{2})";
+ break;
+ case "zz":
+ case "z":
+ add = "([+-]?\\d\\d?)";
+ break;
+ case "/":
+ add = "(\\/)";
+ break;
+ default:
+ throw "Invalid date format pattern \'" + m + "\'.";
+ }
+ if (add) {
+ regexp.push(add);
+ }
+ groups.push(match[0]);
+ }
+ appendPreOrPostMatch(expFormat.slice(index), regexp);
+ regexp.push("$");
+
+ // allow whitespace to differ when matching formats.
+ var regexpStr = regexp.join("").replace(/\s+/g, "\\s+"),
+ parseRegExp = { "regExp": regexpStr, "groups": groups };
+
+ // cache the regex for this format.
+ return re[ format ] = parseRegExp;
+ };
+
+ outOfRange = function (value, low, high) {
+ return value < low || value > high;
+ };
+
+ toUpper = function (value) {
+ // "he-IL" has non-breaking space in weekday names.
+ return value.split("\u00A0").join(" ").toUpperCase();
+ };
+
+ toUpperArray = function (arr) {
+ var results = [];
+ for (var i = 0, l = arr.length; i < l; i++) {
+ results[ i ] = toUpper(arr[i]);
+ }
+ return results;
+ };
+
+ parseExact = function (value, format, culture) {
+ // try to parse the date string by matching against the format string
+ // while using the specified culture for date field names.
+ value = trim(value);
+ var cal = culture.calendar,
+ // convert date formats into regular expressions with groupings.
+ // use the regexp to determine the input format and extract the date fields.
+ parseInfo = getParseRegExp(cal, format),
+ match = new RegExp(parseInfo.regExp).exec(value);
+ if (match === null) {
+ return null;
+ }
+ // found a date format that matches the input.
+ var groups = parseInfo.groups,
+ era = null, year = null, month = null, date = null, weekDay = null,
+ hour = 0, hourOffset, min = 0, sec = 0, msec = 0, tzMinOffset = null,
+ pmHour = false;
+ // iterate the format groups to extract and set the date fields.
+ for (var j = 0, jl = groups.length; j < jl; j++) {
+ var matchGroup = match[ j + 1 ];
+ if (matchGroup) {
+ var current = groups[ j ],
+ clength = current.length,
+ matchInt = parseInt(matchGroup, 10);
+ switch (current) {
+ case "dd":
+ case "d":
+ // Day of month.
+ date = matchInt;
+ // check that date is generally in valid range, also checking overflow below.
+ if (outOfRange(date, 1, 31)) return null;
+ break;
+ case "MMM":
+ case "MMMM":
+ month = getMonthIndex(cal, matchGroup, clength === 3);
+ if (outOfRange(month, 0, 11)) return null;
+ break;
+ case "M":
+ case "MM":
+ // Month.
+ month = matchInt - 1;
+ if (outOfRange(month, 0, 11)) return null;
+ break;
+ case "y":
+ case "yy":
+ case "yyyy":
+ year = clength < 4 ? expandYear(cal, matchInt) : matchInt;
+ if (outOfRange(year, 0, 9999)) return null;
+ break;
+ case "h":
+ case "hh":
+ // Hours (12-hour clock).
+ hour = matchInt;
+ if (hour === 12) hour = 0;
+ if (outOfRange(hour, 0, 11)) return null;
+ break;
+ case "H":
+ case "HH":
+ // Hours (24-hour clock).
+ hour = matchInt;
+ if (outOfRange(hour, 0, 23)) return null;
+ break;
+ case "m":
+ case "mm":
+ // Minutes.
+ min = matchInt;
+ if (outOfRange(min, 0, 59)) return null;
+ break;
+ case "s":
+ case "ss":
+ // Seconds.
+ sec = matchInt;
+ if (outOfRange(sec, 0, 59)) return null;
+ break;
+ case "tt":
+ case "t":
+ // AM/PM designator.
+ // see if it is standard, upper, or lower case PM. If not, ensure it is at least one of
+ // the AM tokens. If not, fail the parse for this format.
+ pmHour = cal.PM && ( matchGroup === cal.PM[0] || matchGroup === cal.PM[1] || matchGroup === cal.PM[2] );
+ if (
+ !pmHour && (
+ !cal.AM || ( matchGroup !== cal.AM[0] && matchGroup !== cal.AM[1] && matchGroup !== cal.AM[2] )
+ )
+ ) return null;
+ break;
+ case "f":
+ // Deciseconds.
+ case "ff":
+ // Centiseconds.
+ case "fff":
+ // Milliseconds.
+ msec = matchInt * Math.pow(10, 3 - clength);
+ if (outOfRange(msec, 0, 999)) return null;
+ break;
+ case "ddd":
+ // Day of week.
+ case "dddd":
+ // Day of week.
+ weekDay = getDayIndex(cal, matchGroup, clength === 3);
+ if (outOfRange(weekDay, 0, 6)) return null;
+ break;
+ case "zzz":
+ // Time zone offset in +/- hours:min.
+ var offsets = matchGroup.split(/:/);
+ if (offsets.length !== 2) return null;
+ hourOffset = parseInt(offsets[0], 10);
+ if (outOfRange(hourOffset, -12, 13)) return null;
+ var minOffset = parseInt(offsets[1], 10);
+ if (outOfRange(minOffset, 0, 59)) return null;
+ tzMinOffset = ( hourOffset * 60 ) + ( startsWith(matchGroup, "-") ? -minOffset : minOffset );
+ break;
+ case "z":
+ case "zz":
+ // Time zone offset in +/- hours.
+ hourOffset = matchInt;
+ if (outOfRange(hourOffset, -12, 13)) return null;
+ tzMinOffset = hourOffset * 60;
+ break;
+ case "g":
+ case "gg":
+ var eraName = matchGroup;
+ if (!eraName || !cal.eras) return null;
+ eraName = trim(eraName.toLowerCase());
+ for (var i = 0, l = cal.eras.length; i < l; i++) {
+ if (eraName === cal.eras[i].name.toLowerCase()) {
+ era = i;
+ break;
+ }
+ }
+ // could not find an era with that name
+ if (era === null) return null;
+ break;
+ }
+ }
+ }
+ var result = new Date(), defaultYear, convert = cal.convert;
+ defaultYear = convert ? convert.fromGregorian(result)[ 0 ] : result.getFullYear();
+ if (year === null) {
+ year = defaultYear;
+ }
+ else if (cal.eras) {
+ // year must be shifted to normal gregorian year
+ // but not if year was not specified, its already normal gregorian
+ // per the main if clause above.
+ year += cal.eras[( era || 0 )].offset;
+ }
+ // set default day and month to 1 and January, so if unspecified, these are the defaults
+ // instead of the current day/month.
+ if (month === null) {
+ month = 0;
+ }
+ if (date === null) {
+ date = 1;
+ }
+ // now have year, month, and date, but in the culture's calendar.
+ // convert to gregorian if necessary
+ if (convert) {
+ result = convert.toGregorian(year, month, date);
+ // conversion failed, must be an invalid match
+ if (result === null) return null;
+ }
+ else {
+ // have to set year, month and date together to avoid overflow based on current date.
+ result.setFullYear(year, month, date);
+ // check to see if date overflowed for specified month (only checked 1-31 above).
+ if (result.getDate() !== date) return null;
+ // invalid day of week.
+ if (weekDay !== null && result.getDay() !== weekDay) {
+ return null;
+ }
+ }
+ // if pm designator token was found make sure the hours fit the 24-hour clock.
+ if (pmHour && hour < 12) {
+ hour += 12;
+ }
+ result.setHours(hour, min, sec, msec);
+ if (tzMinOffset !== null) {
+ // adjust timezone to utc before applying local offset.
+ var adjustedMin = result.getMinutes() - ( tzMinOffset + result.getTimezoneOffset() );
+ // Safari limits hours and minutes to the range of -127 to 127. We need to use setHours
+ // to ensure both these fields will not exceed this range. adjustedMin will range
+ // somewhere between -1440 and 1500, so we only need to split this into hours.
+ result.setHours(result.getHours() + parseInt(adjustedMin / 60, 10), adjustedMin % 60);
+ }
+ return result;
+ };
+ }());
+
+ parseNegativePattern = function (value, nf, negativePattern) {
+ var neg = nf[ "-" ],
+ pos = nf[ "+" ],
+ ret;
+ switch (negativePattern) {
+ case "n -":
+ neg = " " + neg;
+ pos = " " + pos;
+ /* falls through */
+ case "n-":
+ if (endsWith(value, neg)) {
+ ret = [ "-", value.substr(0, value.length - neg.length) ];
+ }
+ else if (endsWith(value, pos)) {
+ ret = [ "+", value.substr(0, value.length - pos.length) ];
+ }
+ break;
+ case "- n":
+ neg += " ";
+ pos += " ";
+ /* falls through */
+ case "-n":
+ if (startsWith(value, neg)) {
+ ret = [ "-", value.substr(neg.length) ];
+ }
+ else if (startsWith(value, pos)) {
+ ret = [ "+", value.substr(pos.length) ];
+ }
+ break;
+ case "(n)":
+ if (startsWith(value, "(") && endsWith(value, ")")) {
+ ret = [ "-", value.substr(1, value.length - 2) ];
+ }
+ break;
+ }
+ return ret || [ "", value ];
+ };
+
+//
+// public instance functions
+//
+
+ Globalize.prototype.findClosestCulture = function (cultureSelector) {
+ return Globalize.findClosestCulture.call(this, cultureSelector);
+ };
+
+ Globalize.prototype.format = function (value, format, cultureSelector) {
+ return Globalize.format.call(this, value, format, cultureSelector);
+ };
+
+ Globalize.prototype.localize = function (key, cultureSelector) {
+ return Globalize.localize.call(this, key, cultureSelector);
+ };
+
+ Globalize.prototype.parseInt = function (value, radix, cultureSelector) {
+ return Globalize.parseInt.call(this, value, radix, cultureSelector);
+ };
+
+ Globalize.prototype.parseFloat = function (value, radix, cultureSelector) {
+ return Globalize.parseFloat.call(this, value, radix, cultureSelector);
+ };
+
+ Globalize.prototype.culture = function (cultureSelector) {
+ return Globalize.culture.call(this, cultureSelector);
+ };
+
+//
+// public singleton functions
+//
+
+ Globalize.addCultureInfo = function (cultureName, baseCultureName, info) {
+
+ var base = {},
+ isNew = false;
+
+ if (typeof cultureName !== "string") {
+ // cultureName argument is optional string. If not specified, assume info is first
+ // and only argument. Specified info deep-extends current culture.
+ info = cultureName;
+ cultureName = this.culture().name;
+ base = this.cultures[ cultureName ];
+ } else if (typeof baseCultureName !== "string") {
+ // baseCultureName argument is optional string. If not specified, assume info is second
+ // argument. Specified info deep-extends specified culture.
+ // If specified culture does not exist, create by deep-extending default
+ info = baseCultureName;
+ isNew = ( this.cultures[ cultureName ] == null );
+ base = this.cultures[ cultureName ] || this.cultures[ "default" ];
+ } else {
+ // cultureName and baseCultureName specified. Assume a new culture is being created
+ // by deep-extending an specified base culture
+ isNew = true;
+ base = this.cultures[ baseCultureName ];
+ }
+
+ this.cultures[ cultureName ] = extend(true, {},
+ base,
+ info
+ );
+ // Make the standard calendar the current culture if it's a new culture
+ if (isNew) {
+ this.cultures[ cultureName ].calendar = this.cultures[ cultureName ].calendars.standard;
+ }
+ };
+
+ Globalize.findClosestCulture = function (name) {
+ var match;
+ if (!name) {
+ return this.findClosestCulture(this.cultureSelector) || this.cultures[ "default" ];
+ }
+ if (typeof name === "string") {
+ name = name.split(",");
+ }
+ if (isArray(name)) {
+ var lang,
+ cultures = this.cultures,
+ list = name,
+ i, l = list.length,
+ prioritized = [];
+ for (i = 0; i < l; i++) {
+ name = trim(list[i]);
+ var pri, parts = name.split(";");
+ lang = trim(parts[0]);
+ if (parts.length === 1) {
+ pri = 1;
+ }
+ else {
+ name = trim(parts[1]);
+ if (name.indexOf("q=") === 0) {
+ name = name.substr(2);
+ pri = parseFloat(name);
+ pri = isNaN(pri) ? 0 : pri;
+ }
+ else {
+ pri = 1;
+ }
+ }
+ prioritized.push({ lang: lang, pri: pri });
+ }
+ prioritized.sort(function (a, b) {
+ if (a.pri < b.pri) {
+ return 1;
+ } else if (a.pri > b.pri) {
+ return -1;
+ }
+ return 0;
+ });
+ // exact match
+ for (i = 0; i < l; i++) {
+ lang = prioritized[ i ].lang;
+ match = cultures[ lang ];
+ if (match) {
+ return match;
+ }
+ }
+
+ // neutral language match
+ for (i = 0; i < l; i++) {
+ lang = prioritized[ i ].lang;
+ do {
+ var index = lang.lastIndexOf("-");
+ if (index === -1) {
+ break;
+ }
+ // strip off the last part. e.g. en-US => en
+ lang = lang.substr(0, index);
+ match = cultures[ lang ];
+ if (match) {
+ return match;
+ }
+ }
+ while (1);
+ }
+
+ // last resort: match first culture using that language
+ for (i = 0; i < l; i++) {
+ lang = prioritized[ i ].lang;
+ for (var cultureKey in cultures) {
+ var culture = cultures[ cultureKey ];
+ if (culture.language == lang) {
+ return culture;
+ }
+ }
+ }
+ }
+ else if (typeof name === "object") {
+ return name;
+ }
+ return match || null;
+ };
+
+ Globalize.format = function (value, format, cultureSelector) {
+ var culture = this.findClosestCulture(cultureSelector);
+ if (value instanceof Date) {
+ value = formatDate(value, format, culture);
+ }
+ else if (typeof value === "number") {
+ value = formatNumber(value, format, culture);
+ }
+ return value;
+ };
+
+ Globalize.localize = function (key, cultureSelector) {
+ return this.findClosestCulture(cultureSelector).messages[ key ] ||
+ this.cultures[ "default" ].messages[ key ];
+ };
+
+ Globalize.parseDate = function (value, formats, culture) {
+ culture = this.findClosestCulture(culture);
+
+ var date, prop, patterns;
+ if (formats) {
+ if (typeof formats === "string") {
+ formats = [ formats ];
+ }
+ if (formats.length) {
+ for (var i = 0, l = formats.length; i < l; i++) {
+ var format = formats[ i ];
+ if (format) {
+ date = parseExact(value, format, culture);
+ if (date) {
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ patterns = culture.calendar.patterns;
+ for (prop in patterns) {
+ date = parseExact(value, patterns[prop], culture);
+ if (date) {
+ break;
+ }
+ }
+ }
+
+ return date || null;
+ };
+
+ Globalize.parseInt = function (value, radix, cultureSelector) {
+ return truncate(Globalize.parseFloat(value, radix, cultureSelector));
+ };
+
+ Globalize.parseFloat = function (value, radix, cultureSelector) {
+ // radix argument is optional
+ if (typeof radix !== "number") {
+ cultureSelector = radix;
+ radix = 10;
+ }
+
+ var culture = this.findClosestCulture(cultureSelector);
+ var ret = NaN,
+ nf = culture.numberFormat;
+
+ if (value.indexOf(culture.numberFormat.currency.symbol) > -1) {
+ // remove currency symbol
+ value = value.replace(culture.numberFormat.currency.symbol, "");
+ // replace decimal seperator
+ value = value.replace(culture.numberFormat.currency["."], culture.numberFormat["."]);
+ }
+
+ //Remove percentage character from number string before parsing
+ if (value.indexOf(culture.numberFormat.percent.symbol) > -1) {
+ value = value.replace(culture.numberFormat.percent.symbol, "");
+ }
+
+ // remove spaces: leading, trailing and between - and number. Used for negative currency pt-BR
+ value = value.replace(/ /g, "");
+
+ // allow infinity or hexidecimal
+ if (regexInfinity.test(value)) {
+ ret = parseFloat(value);
+ }
+ else if (!radix && regexHex.test(value)) {
+ ret = parseInt(value, 16);
+ }
+ else {
+
+ // determine sign and number
+ var signInfo = parseNegativePattern(value, nf, nf.pattern[0]),
+ sign = signInfo[ 0 ],
+ num = signInfo[ 1 ];
+
+ // #44 - try parsing as "(n)"
+ if (sign === "" && nf.pattern[0] !== "(n)") {
+ signInfo = parseNegativePattern(value, nf, "(n)");
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ // try parsing as "-n"
+ if (sign === "" && nf.pattern[0] !== "-n") {
+ signInfo = parseNegativePattern(value, nf, "-n");
+ sign = signInfo[ 0 ];
+ num = signInfo[ 1 ];
+ }
+
+ sign = sign || "+";
+
+ // determine exponent and number
+ var exponent,
+ intAndFraction,
+ exponentPos = num.indexOf("e");
+ if (exponentPos < 0) exponentPos = num.indexOf("E");
+ if (exponentPos < 0) {
+ intAndFraction = num;
+ exponent = null;
+ }
+ else {
+ intAndFraction = num.substr(0, exponentPos);
+ exponent = num.substr(exponentPos + 1);
+ }
+ // determine decimal position
+ var integer,
+ fraction,
+ decSep = nf[ "." ],
+ decimalPos = intAndFraction.indexOf(decSep);
+ if (decimalPos < 0) {
+ integer = intAndFraction;
+ fraction = null;
+ }
+ else {
+ integer = intAndFraction.substr(0, decimalPos);
+ fraction = intAndFraction.substr(decimalPos + decSep.length);
+ }
+ // handle groups (e.g. 1,000,000)
+ var groupSep = nf[ "," ];
+ integer = integer.split(groupSep).join("");
+ var altGroupSep = groupSep.replace(/\u00A0/g, " ");
+ if (groupSep !== altGroupSep) {
+ integer = integer.split(altGroupSep).join("");
+ }
+ // build a natively parsable number string
+ var p = sign + integer;
+ if (fraction !== null) {
+ p += "." + fraction;
+ }
+ if (exponent !== null) {
+ // exponent itself may have a number patternd
+ var expSignInfo = parseNegativePattern(exponent, nf, "-n");
+ p += "e" + ( expSignInfo[0] || "+" ) + expSignInfo[ 1 ];
+ }
+ if (regexParseFloat.test(p)) {
+ ret = parseFloat(p);
+ }
+ }
+ return ret;
+ };
+
+ Globalize.culture = function (cultureSelector) {
+ // setter
+ if (typeof cultureSelector !== "undefined") {
+ this.cultureSelector = cultureSelector;
+ }
+ // getter
+ return this.findClosestCulture(cultureSelector) || this.cultures[ "default" ];
+ };
+
+}(this));
\ No newline at end of file
diff --git a/static/js/old__dss_sound_handler.js b/static/js/old__dss_sound_handler.js
deleted file mode 100644
index 0e17222..0000000
--- a/static/js/old__dss_sound_handler.js
+++ /dev/null
@@ -1,135 +0,0 @@
-function DssSoundHandler() {
- var _currentSound = null;
- var _currentId = -1;
- this.stop_sound = function () {
- if (_currentSound) {
- this.togglePlaying(this.getPlayingId());
- _currentSound.stop();
- _currentId = -1;
- }
- };
- this.getPlayingId = function () {
- return _currentId;
- };
- this.isPlaying = function () {
- if (_currentSound != null)
- return _currentSound.playState == 1;
- };
-
- this.togglePlaying = function (id) {
- this.togglePlayState(id);
- return this.togglePlayVisual(id);
- };
-
- this.togglePlayVisual = function (id) {
- var button = $('#play-pause-button-small-' + id);
- var mode = button.data("mode");
- if (mode == "play" || mode == "resume") {
- button.data('mode', 'pause');
- button.removeClass('play-button-small-playing');
- button.addClass('play-button-small-paused');
- } else {
- button.data('mode', 'resume');
- button.removeClass('play-button-small-paused');
- button.addClass('play-button-small-playing');
- }
- return mode;
- };
- this.togglePlayState = function (id) {
- var button = $('#play-pause-button-small-' + id);
- var mode = button.data("mode");
- if (mode == 'pause')
- this.pauseSound();
- else if (mode == 'resume')
- this.resumeSound();
- };
- this.playSound = (function (itemId, stream_url) {
-
- _currentId = itemId;
- var waveformTop = $('#waveform-' + _currentId).position().top;
- var waveformWidth = $('#waveform-' + _currentId).width();
-
- $('#player-seekhead').css('top', waveformTop);
- if (_currentSound) _currentSound.stop();
- soundManager.destroySound('current_sound');
- _currentSound = soundManager.createSound({
- id:'current_sound',
- url:stream_url,
- volume:50,
- stream:true,
- whileloading:function () {
- var percentageFinished = (_currentSound.bytesLoaded / _currentSound.bytesTotal) * 100;
- var percentageWidth = (waveformWidth / 100) * percentageFinished;
- $('#progress-player-' + _currentId).css('width', percentageWidth);
- },
- whileplaying:function () {
- /* Should move to an aggregator viewChanged callback */
- if (_currentId == -1){
- _currentId = itemId;
- this._setupEvents(itemId, _currentSound);
- }
- waveformTop = $('#waveform-' + _currentId).position().top;
- waveformWidth = $('#waveform-' + _currentId).width();
- $('#playhead-player-' + _currentId).css('top', waveformTop);
- $('#progress-player-' + _currentId).css('top', waveformTop);
- $('#player-seekhead').css('top', waveformTop);
-
- var currentPosition = _currentSound.position;
- var totalLength = _currentSound.duration;
- var percentageFinished = (currentPosition / totalLength) * 100;
- var percentageWidth = (waveformWidth / 100) * percentageFinished;
- $('#playhead-player-' + _currentId).css('width', percentageWidth);
- }
- });
- _currentSound.loaded = false;
- _currentSound.readyState = 0;
- dssSoundHandler._setupEvents(_currentId, _currentSound);
- _currentSound.play();
- });
- this.playLive = function () {
- this.stop_sound();
- _currentSound = soundManager.createSound({
- id:'current_sound',
- url:com.podnoms.settings.liveStreamRoot,
- volume:50,
- stream:true,
- useMovieStar:true
- });
- _currentSound.play();
- };
- this.pauseSound = function () {
- this.togglePlaying();
- if (_currentSound) {
- _currentSound.pause();
- }
- };
- this.resumeSound = function () {
- this.togglePlaying();
- if (_currentSound)
- _currentSound.resume();
- };
- this.getPosition = function () {
- if (_currentSound)
- return _currentSound.position;
- };
- this._setupEvents = function (itemId, sound) {
- $('#waveform-' + itemId).mousemove(function (event) {
- $('#player-seekhead').show();
- $('#player-seekhead').css('left', (event.pageX) + 'px').fadeIn('fast');
- });
- $('#waveform-' + itemId).mousedown(function (event) {
- var width = $('#waveform-image-' + itemId).width();
- if (sound != null) {
- var fullLength = sound.duration;
- var left = $('#waveform-image-' + itemId).offset().left;
- var clickPerc = ((event.pageX - left) / width) * 100;
- sound.setPosition((fullLength / 100) * clickPerc);
- }
- });
- $('#waveform-' + itemId).mouseleave(function (event) {
- $('#player-seekhead').hide();
- });
- };
-}
-dssSoundHandler = new DssSoundHandler();
-window.dssSoundHandler = dssSoundHandler;
diff --git a/templates/base.html b/templates/base.html
index c76f677..1f8bcd2 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -96,6 +96,7 @@
+
diff --git a/templates/views/ActivityListItemView.html b/templates/views/ActivityListItemView.html
index 367294d..740af0c 100644
--- a/templates/views/ActivityListItemView.html
+++ b/templates/views/ActivityListItemView.html
@@ -1,17 +1,17 @@
-{% load account_tags %}
-
-