/* Minification failed. Returning unminified contents. (1909,142-149): run-time error JS1019: Can't have 'break' outside of loop: break n (1908,451-458): run-time error JS1019: Can't have 'break' outside of loop: break n */ /** * Copyright 2010 Tim Down. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * jshashtable * * jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable * in the global scope. * * Author: Tim Down * Version: 2.1 * Build date: 21 March 2010 * Website: http://www.timdown.co.uk/jshashtable */ var Hashtable = (function() { var FUNCTION = "function"; var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ? function(arr, idx) { arr.splice(idx, 1); } : function(arr, idx) { var itemsAfterDeleted, i, len; if (idx === arr.length - 1) { arr.length = idx; } else { itemsAfterDeleted = arr.slice(idx + 1); arr.length = idx; for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) { arr[idx + i] = itemsAfterDeleted[i]; } } }; function hashObject(obj) { var hashCode; if (typeof obj == "string") { return obj; } else if (typeof obj.hashCode == FUNCTION) { // Check the hashCode method really has returned a string hashCode = obj.hashCode(); return (typeof hashCode == "string") ? hashCode : hashObject(hashCode); } else if (typeof obj.toString == FUNCTION) { return obj.toString(); } else { try { return String(obj); } catch (ex) { // For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when // passed to String() return Object.prototype.toString.call(obj); } } } function equals_fixedValueHasEquals(fixedValue, variableValue) { return fixedValue.equals(variableValue); } function equals_fixedValueNoEquals(fixedValue, variableValue) { return (typeof variableValue.equals == FUNCTION) ? variableValue.equals(fixedValue) : (fixedValue === variableValue); } function createKeyValCheck(kvStr) { return function(kv) { if (kv === null) { throw new Error("null is not a valid " + kvStr); } else if (typeof kv == "undefined") { throw new Error(kvStr + " must not be undefined"); } }; } var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value"); /*----------------------------------------------------------------------------------------------------------------*/ function Bucket(hash, firstKey, firstValue, equalityFunction) { this[0] = hash; this.entries = []; this.addEntry(firstKey, firstValue); if (equalityFunction !== null) { this.getEqualityFunction = function() { return equalityFunction; }; } } var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2; function createBucketSearcher(mode) { return function(key) { var i = this.entries.length, entry, equals = this.getEqualityFunction(key); while (i--) { entry = this.entries[i]; if ( equals(key, entry[0]) ) { switch (mode) { case EXISTENCE: return true; case ENTRY: return entry; case ENTRY_INDEX_AND_VALUE: return [ i, entry[1] ]; } } } return false; }; } function createBucketLister(entryProperty) { return function(aggregatedArr) { var startIndex = aggregatedArr.length; for (var i = 0, len = this.entries.length; i < len; ++i) { aggregatedArr[startIndex + i] = this.entries[i][entryProperty]; } }; } Bucket.prototype = { getEqualityFunction: function(searchValue) { return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals; }, getEntryForKey: createBucketSearcher(ENTRY), getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE), removeEntryForKey: function(key) { var result = this.getEntryAndIndexForKey(key); if (result) { arrayRemoveAt(this.entries, result[0]); return result[1]; } return null; }, addEntry: function(key, value) { this.entries[this.entries.length] = [key, value]; }, keys: createBucketLister(0), values: createBucketLister(1), getEntries: function(entries) { var startIndex = entries.length; for (var i = 0, len = this.entries.length; i < len; ++i) { // Clone the entry stored in the bucket before adding to array entries[startIndex + i] = this.entries[i].slice(0); } }, containsKey: createBucketSearcher(EXISTENCE), containsValue: function(value) { var i = this.entries.length; while (i--) { if ( value === this.entries[i][1] ) { return true; } } return false; } }; /*----------------------------------------------------------------------------------------------------------------*/ // Supporting functions for searching hashtable buckets function searchBuckets(buckets, hash) { var i = buckets.length, bucket; while (i--) { bucket = buckets[i]; if (hash === bucket[0]) { return i; } } return null; } function getBucketForHash(bucketsByHash, hash) { var bucket = bucketsByHash[hash]; // Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype return ( bucket && (bucket instanceof Bucket) ) ? bucket : null; } /*----------------------------------------------------------------------------------------------------------------*/ function Hashtable(hashingFunctionParam, equalityFunctionParam) { var that = this; var buckets = []; var bucketsByHash = {}; var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject; var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null; this.put = function(key, value) { checkKey(key); checkValue(value); var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null; // Check if a bucket exists for the bucket key bucket = getBucketForHash(bucketsByHash, hash); if (bucket) { // Check this bucket to see if it already contains this key bucketEntry = bucket.getEntryForKey(key); if (bucketEntry) { // This bucket entry is the current mapping of key to value, so replace old value and we're done. oldValue = bucketEntry[1]; bucketEntry[1] = value; } else { // The bucket does not contain an entry for this key, so add one bucket.addEntry(key, value); } } else { // No bucket exists for the key, so create one and put our key/value mapping in bucket = new Bucket(hash, key, value, equalityFunction); buckets[buckets.length] = bucket; bucketsByHash[hash] = bucket; } return oldValue; }; this.get = function(key) { checkKey(key); var hash = hashingFunction(key); // Check if a bucket exists for the bucket key var bucket = getBucketForHash(bucketsByHash, hash); if (bucket) { // Check this bucket to see if it contains this key var bucketEntry = bucket.getEntryForKey(key); if (bucketEntry) { // This bucket entry is the current mapping of key to value, so return the value. return bucketEntry[1]; } } return null; }; this.containsKey = function(key) { checkKey(key); var bucketKey = hashingFunction(key); // Check if a bucket exists for the bucket key var bucket = getBucketForHash(bucketsByHash, bucketKey); return bucket ? bucket.containsKey(key) : false; }; this.containsValue = function(value) { checkValue(value); var i = buckets.length; while (i--) { if (buckets[i].containsValue(value)) { return true; } } return false; }; this.clear = function() { buckets.length = 0; bucketsByHash = {}; }; this.isEmpty = function() { return !buckets.length; }; var createBucketAggregator = function(bucketFuncName) { return function() { var aggregated = [], i = buckets.length; while (i--) { buckets[i][bucketFuncName](aggregated); } return aggregated; }; }; this.keys = createBucketAggregator("keys"); this.values = createBucketAggregator("values"); this.entries = createBucketAggregator("getEntries"); this.remove = function(key) { checkKey(key); var hash = hashingFunction(key), bucketIndex, oldValue = null; // Check if a bucket exists for the bucket key var bucket = getBucketForHash(bucketsByHash, hash); if (bucket) { // Remove entry from this bucket for this key oldValue = bucket.removeEntryForKey(key); if (oldValue !== null) { // Entry was removed, so check if bucket is empty if (!bucket.entries.length) { // Bucket is empty, so remove it from the bucket collections bucketIndex = searchBuckets(buckets, hash); arrayRemoveAt(buckets, bucketIndex); delete bucketsByHash[hash]; } } } return oldValue; }; this.size = function() { var total = 0, i = buckets.length; while (i--) { total += buckets[i].entries.length; } return total; }; this.each = function(callback) { var entries = that.entries(), i = entries.length, entry; while (i--) { entry = entries[i]; callback(entry[0], entry[1]); } }; this.putAll = function(hashtable, conflictCallback) { var entries = hashtable.entries(); var entry, key, value, thisValue, i = entries.length; var hasConflictCallback = (typeof conflictCallback == FUNCTION); while (i--) { entry = entries[i]; key = entry[0]; value = entry[1]; // Check for a conflict. The default behaviour is to overwrite the value for an existing key if ( hasConflictCallback && (thisValue = that.get(key)) ) { value = conflictCallback(key, thisValue, value); } that.put(key, value); } }; this.clone = function() { var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam); clone.putAll(that); return clone; }; } return Hashtable; })(); /** * jquery.numberformatter - Formatting/Parsing Numbers in jQuery * * Written by * Michael Abernethy (mike@abernethysoft.com), * Andrew Parry (aparry0@gmail.com) * * Dual licensed under the MIT (MIT-LICENSE.txt) * and GPL (GPL-LICENSE.txt) licenses. * * @author Michael Abernethy, Andrew Parry * @version 1.2.3-SNAPSHOT ($Id$) * * Dependencies * * jQuery (http://jquery.com) * jshashtable (http://www.timdown.co.uk/jshashtable) * * Notes & Thanks * * many thanks to advweb.nanasi.jp for his bug fixes * jsHashtable is now used also, so thanks to the author for that excellent little class. * * This plugin can be used to format numbers as text and parse text as Numbers * Because we live in an international world, we cannot assume that everyone * uses "," to divide thousands, and "." as a decimal point. * * As of 1.2 the way this plugin works has changed slightly, parsing text to a number * has 1 set of functions, formatting a number to text has it's own. Before things * were a little confusing, so I wanted to separate the 2 out more. * * * jQuery extension functions: * * formatNumber(options, writeBack, giveReturnValue) - Reads the value from the subject, parses to * a Javascript Number object, then formats back to text using the passed options and write back to * the subject. * * parseNumber(options) - Parses the value in the subject to a Number object using the passed options * to decipher the actual number from the text, then writes the value as text back to the subject. * * * Generic functions: * * formatNumber(numberString, options) - Takes a plain number as a string (e.g. '1002.0123') and returns * a string of the given format options. * * parseNumber(numberString, options) - Takes a number as text that is formatted the same as the given * options then and returns it as a plain Number object. * * To achieve the old way of combining parsing and formatting to keep say a input field always formatted * to a given format after it has lost focus you'd simply use a combination of the functions. * * e.g. * $("#salary").blur(function(){ * $(this).parseNumber({format:"#,###.00", locale:"us"}); * $(this).formatNumber({format:"#,###.00", locale:"us"}); * }); * * The syntax for the formatting is: * 0 = Digit * # = Digit, zero shows as absent * . = Decimal separator * - = Negative sign * , = Grouping Separator * % = Percent (multiplies the number by 100) * * For example, a format of "#,###.00" and text of 4500.20 will * display as "4.500,20" with a locale of "de", and "4,500.20" with a locale of "us" * * * As of now, the only acceptable locales are * Arab Emirates -> "ae" * Australia -> "au" * Austria -> "at" * Brazil -> "br" * Canada -> "ca" * China -> "cn" * Czech -> "cz" * Denmark -> "dk" * Egypt -> "eg" * Finland -> "fi" * France -> "fr" * Germany -> "de" * Greece -> "gr" * Great Britain -> "gb" * Hong Kong -> "hk" * India -> "in" * Israel -> "il" * Japan -> "jp" * Russia -> "ru" * South Korea -> "kr" * Spain -> "es" * Sweden -> "se" * Switzerland -> "ch" * Taiwan -> "tw" * Thailand -> "th" * United States -> "us" * Vietnam -> "vn" **/ (function(jQuery) { var nfLocales = new Hashtable(); var nfLocalesLikeUS = [ 'ae','au','ca','cn','eg','gb','hk','il','in','jp','sk','th','tw','us' ]; var nfLocalesLikeDE = [ 'at','br','de','dk','es','gr','it','nl','pt','tr','vn' ]; var nfLocalesLikeFR = [ 'cz','fi','fr','ru','se','pl' ]; var nfLocalesLikeCH = [ 'ch' ]; var nfLocaleFormatting = [ [".", ","], [",", "."], [",", " "], [".", "'"] ]; var nfAllLocales = [ nfLocalesLikeUS, nfLocalesLikeDE, nfLocalesLikeFR, nfLocalesLikeCH ] function FormatData(dec, group, neg) { this.dec = dec; this.group = group; this.neg = neg; }; function init() { // write the arrays into the hashtable for (var localeGroupIdx = 0; localeGroupIdx < nfAllLocales.length; localeGroupIdx++) { localeGroup = nfAllLocales[localeGroupIdx]; for (var i = 0; i < localeGroup.length; i++) { nfLocales.put(localeGroup[i], localeGroupIdx); } } }; function formatCodes(locale, isFullLocale) { if (nfLocales.size() == 0) init(); // default values var dec = "."; var group = ","; var neg = "-"; if (isFullLocale == false) { // Extract and convert to lower-case any language code from a real 'locale' formatted string, if not use as-is // (To prevent locale format like : "fr_FR", "en_US", "de_DE", "fr_FR", "en-US", "de-DE") if (locale.indexOf('_') != -1) locale = locale.split('_')[1].toLowerCase(); else if (locale.indexOf('-') != -1) locale = locale.split('-')[1].toLowerCase(); } // hashtable lookup to match locale with codes var codesIndex = nfLocales.get(locale); if (codesIndex) { var codes = nfLocaleFormatting[codesIndex]; if (codes) { dec = codes[0]; group = codes[1]; } } return new FormatData(dec, group, neg); }; /* Formatting Methods */ /** * Formats anything containing a number in standard js number notation. * * @param {Object} options The formatting options to use * @param {Boolean} writeBack (true) If the output value should be written back to the subject * @param {Boolean} giveReturnValue (true) If the function should return the output string */ jQuery.fn.formatNumber = function(options, writeBack, giveReturnValue) { return this.each(function() { // enforce defaults if (writeBack == null) writeBack = true; if (giveReturnValue == null) giveReturnValue = true; // get text var text; if (jQuery(this).is(":input")) text = new String(jQuery(this).val()); else text = new String(jQuery(this).text()); // format var returnString = jQuery.formatNumber(text, options); // set formatted string back, only if a success // if (returnString) { if (writeBack) { if (jQuery(this).is(":input")) jQuery(this).val(returnString); else jQuery(this).text(returnString); } if (giveReturnValue) return returnString; // } // return ''; }); }; /** * First parses a string and reformats it with the given options. * * @param {Object} numberString * @param {Object} options */ jQuery.formatNumber = function(numberString, options){ var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options); var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); var dec = formatData.dec; var group = formatData.group; var neg = formatData.neg; var validFormat = "0#-,."; // strip all the invalid characters at the beginning and the end // of the format, and we'll stick them back on at the end // make a special case for the negative sign "-" though, so // we can have formats like -$23.32 var prefix = ""; var negativeInFront = false; for (var i = 0; i < options.format.length; i++) { if (validFormat.indexOf(options.format.charAt(i)) == -1) prefix = prefix + options.format.charAt(i); else if (i == 0 && options.format.charAt(i) == '-') { negativeInFront = true; continue; } else break; } var suffix = ""; for (var i = options.format.length - 1; i >= 0; i--) { if (validFormat.indexOf(options.format.charAt(i)) == -1) suffix = options.format.charAt(i) + suffix; else break; } options.format = options.format.substring(prefix.length); options.format = options.format.substring(0, options.format.length - suffix.length); // now we need to convert it into a number //while (numberString.indexOf(group) > -1) // numberString = numberString.replace(group, ''); //var number = new Number(numberString.replace(dec, ".").replace(neg, "-")); var number = new Number(numberString); return jQuery._formatNumber(number, options, suffix, prefix, negativeInFront); }; /** * Formats a Number object into a string, using the given formatting options * * @param {Object} numberString * @param {Object} options */ jQuery._formatNumber = function(number, options, suffix, prefix, negativeInFront) { var options = jQuery.extend({}, jQuery.fn.formatNumber.defaults, options); var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); var dec = formatData.dec; var group = formatData.group; var neg = formatData.neg; var forcedToZero = false; if (isNaN(number)) { if (options.nanForceZero == true) { number = 0; forcedToZero = true; } else return null; } // special case for percentages if (suffix == "%") number = number * 100; var returnString = ""; if (options.format.indexOf(".") > -1) { var decimalPortion = dec; var decimalFormat = options.format.substring(options.format.lastIndexOf(".") + 1); // round or truncate number as needed if (options.round == true) number = new Number(number.toFixed(decimalFormat.length)); else { var numStr = number.toString(); numStr = numStr.substring(0, numStr.lastIndexOf('.') + decimalFormat.length + 1); number = new Number(numStr); } var decimalValue = number % 1; var decimalString = new String(decimalValue.toFixed(decimalFormat.length)); decimalString = decimalString.substring(decimalString.lastIndexOf(".") + 1); for (var i = 0; i < decimalFormat.length; i++) { if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) != '0') { decimalPortion += decimalString.charAt(i); continue; } else if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) == '0') { var notParsed = decimalString.substring(i); if (notParsed.match('[1-9]')) { decimalPortion += decimalString.charAt(i); continue; } else break; } else if (decimalFormat.charAt(i) == "0") decimalPortion += decimalString.charAt(i); } returnString += decimalPortion } else number = Math.round(number); var ones = Math.floor(number); if (number < 0) ones = Math.ceil(number); var onesFormat = ""; if (options.format.indexOf(".") == -1) onesFormat = options.format; else onesFormat = options.format.substring(0, options.format.indexOf(".")); var onePortion = ""; if (!(ones == 0 && onesFormat.substr(onesFormat.length - 1) == '#') || forcedToZero) { // find how many digits are in the group var oneText = new String(Math.abs(ones)); var groupLength = 9999; if (onesFormat.lastIndexOf(",") != -1) groupLength = onesFormat.length - onesFormat.lastIndexOf(",") - 1; var groupCount = 0; for (var i = oneText.length - 1; i > -1; i--) { onePortion = oneText.charAt(i) + onePortion; groupCount++; if (groupCount == groupLength && i != 0) { onePortion = group + onePortion; groupCount = 0; } } // account for any pre-data padding if (onesFormat.length > onePortion.length) { var padStart = onesFormat.indexOf('0'); if (padStart != -1) { var padLen = onesFormat.length - padStart; // pad to left with 0's or group char var pos = onesFormat.length - onePortion.length - 1; while (onePortion.length < padLen) { var padChar = onesFormat.charAt(pos); // replace with real group char if needed if (padChar == ',') padChar = group; onePortion = padChar + onePortion; pos--; } } } } if (!onePortion && onesFormat.indexOf('0', onesFormat.length - 1) !== -1) onePortion = '0'; returnString = onePortion + returnString; // handle special case where negative is in front of the invalid characters if (number < 0 && negativeInFront && prefix.length > 0) prefix = neg + prefix; else if (number < 0) returnString = neg + returnString; if (!options.decimalSeparatorAlwaysShown) { if (returnString.lastIndexOf(dec) == returnString.length - 1) { returnString = returnString.substring(0, returnString.length - 1); } } returnString = prefix + returnString + suffix; return returnString; }; /* Parsing Methods */ /** * Parses a number of given format from the element and returns a Number object. * @param {Object} options */ jQuery.fn.parseNumber = function(options, writeBack, giveReturnValue) { // enforce defaults if (writeBack == null) writeBack = true; if (giveReturnValue == null) giveReturnValue = true; // get text var text; if (jQuery(this).is(":input")) text = new String(jQuery(this).val()); else text = new String(jQuery(this).text()); // parse text var number = jQuery.parseNumber(text, options); if (number) { if (writeBack) { if (jQuery(this).is(":input")) jQuery(this).val(number.toString()); else jQuery(this).text(number.toString()); } if (giveReturnValue) return number; } }; /** * Parses a string of given format into a Number object. * * @param {Object} string * @param {Object} options */ jQuery.parseNumber = function(numberString, options) { var options = jQuery.extend({}, jQuery.fn.parseNumber.defaults, options); var formatData = formatCodes(options.locale.toLowerCase(), options.isFullLocale); var dec = formatData.dec; var group = formatData.group; var neg = formatData.neg; var valid = "1234567890.-"; // now we need to convert it into a number while (numberString.indexOf(group)>-1) numberString = numberString.replace(group,''); numberString = numberString.replace(dec,".").replace(neg,"-"); var validText = ""; var hasPercent = false; if (numberString.charAt(numberString.length - 1) == "%" || options.isPercentage == true) hasPercent = true; for (var i=0; i-1) validText = validText + numberString.charAt(i); } var number = new Number(validText); if (hasPercent) { number = number / 100; var decimalPos = validText.indexOf('.'); if (decimalPos != -1) { var decimalPoints = validText.length - decimalPos - 1; number = number.toFixed(decimalPoints + 2); } else { number = number.toFixed(validText.length - 1); } } return number; }; jQuery.fn.parseNumber.defaults = { locale: "us", decimalSeparatorAlwaysShown: false, isPercentage: false, isFullLocale: false }; jQuery.fn.formatNumber.defaults = { format: "#,###.00", locale: "us", decimalSeparatorAlwaysShown: false, nanForceZero: true, round: true, isFullLocale: false }; Number.prototype.toFixed = function(precision) { return jQuery._roundNumber(this, precision); }; jQuery._roundNumber = function(number, decimalPlaces) { var power = Math.pow(10, decimalPlaces || 0); var value = String(Math.round(number * power) / power); // ensure the decimal places are there if (decimalPlaces > 0) { var dp = value.indexOf("."); if (dp == -1) { value += '.'; dp = 0; } else { dp = value.length - (dp + 1); } while (dp < decimalPlaces) { value += '0'; dp++; } } return value; }; })(jQuery); // Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed (function(){ var cache = {}; this.tmpl = function tmpl(str, data){ // Figure out if we're getting a template, or if we need to // load the template - and be sure to cache the result. var fn = !/\W/.test(str) ? cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) : // Generate a reusable function that will serve as a template // generator (and which will be cached). new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){} "with(obj){p.push('" + // Convert the template into pure JavaScript str .replace(/[\r\t\n]/g, " ") .split("<%").join("\t") .replace(/((^|%>)[^\t]*)'/g, "$1\r") .replace(/\t=(.*?)%>/g, "',$1,'") .split("\t").join("');") .split("%>").join("p.push('") .split("\r").join("\\'") + "');}return p.join('');"); // Provide some basic currying to the user return data ? fn( data ) : fn; }; })(); /** * jquery.dependClass - Attach class based on first class in list of current element * * Written by * Egor Khmelev (hmelyoff@gmail.com) * * Licensed under the MIT (MIT-LICENSE.txt). * * @author Egor Khmelev * @version 0.1.0-BETA ($Id$) * **/ (function($) { $.baseClass = function(obj){ obj = $(obj); return obj.get(0).className.match(/([^ ]+)/)[1]; }; $.fn.addDependClass = function(className, delimiter){ var options = { delimiter: delimiter ? delimiter : '-' } return this.each(function(){ var baseClass = $.baseClass(this); if(baseClass) $(this).addClass(baseClass + options.delimiter + className); }); }; $.fn.removeDependClass = function(className, delimiter){ var options = { delimiter: delimiter ? delimiter : '-' } return this.each(function(){ var baseClass = $.baseClass(this); if(baseClass) $(this).removeClass(baseClass + options.delimiter + className); }); }; $.fn.toggleDependClass = function(className, delimiter){ var options = { delimiter: delimiter ? delimiter : '-' } return this.each(function(){ var baseClass = $.baseClass(this); if(baseClass) if($(this).is("." + baseClass + options.delimiter + className)) $(this).removeClass(baseClass + options.delimiter + className); else $(this).addClass(baseClass + options.delimiter + className); }); }; })(jQuery); /** * draggable - Class allows to make any element draggable * * Written by * Egor Khmelev (hmelyoff@gmail.com) * * Licensed under the MIT (MIT-LICENSE.txt). * * @author Egor Khmelev * @version 0.1.0-BETA ($Id$) * **/ (function( $ ){ function Draggable(){ this._init.apply( this, arguments ); }; Draggable.prototype.oninit = function(){ }; Draggable.prototype.events = function(){ }; Draggable.prototype.onmousedown = function(){ this.ptr.css({ position: "absolute" }); }; Draggable.prototype.onmousemove = function( evt, x, y ){ this.ptr.css({ left: x, top: y }); }; Draggable.prototype.onmouseup = function(){ }; Draggable.prototype.isDefault = { drag: false, clicked: false, toclick: true, mouseup: false }; Draggable.prototype._init = function(){ if( arguments.length > 0 ){ this.ptr = $(arguments[0]); this.outer = $(".draggable-outer"); this.is = {}; $.extend( this.is, this.isDefault ); var _offset = this.ptr.offset(); this.d = { left: _offset.left, top: _offset.top, width: this.ptr.width(), height: this.ptr.height() }; this.oninit.apply( this, arguments ); this._events(); } }; Draggable.prototype._getPageCoords = function( event ){ if( event.targetTouches && event.targetTouches[0] ){ return { x: event.targetTouches[0].pageX, y: event.targetTouches[0].pageY }; } else return { x: event.pageX, y: event.pageY }; }; Draggable.prototype._bindEvent = function( ptr, eventType, handler ){ var self = this; if( this.supportTouches_ ) ptr.get(0).addEventListener( this.events_[ eventType ], handler, false ); else ptr.bind( this.events_[ eventType ], handler ); }; Draggable.prototype._events = function(){ var self = this; this.supportTouches_ = false;// 'ontouchend' in document; this.events_ = { "click": this.supportTouches_ ? "touchstart" : "click", "down": this.supportTouches_ ? "touchstart" : "mousedown", "move": this.supportTouches_ ? "touchmove" : "mousemove", "up" : this.supportTouches_ ? "touchend" : "mouseup" }; this._bindEvent( $( document ), "move", function( event ){ if( self.is.drag ){ event.stopPropagation(); event.preventDefault(); self._mousemove( event ); } }); this._bindEvent( $( document ), "down", function( event ){ if( self.is.drag ){ event.stopPropagation(); event.preventDefault(); } }); this._bindEvent( $( document ), "up", function( event ){ self._mouseup( event ); }); this._bindEvent( this.ptr, "down", function( event ){ self._mousedown( event ); return false; }); this._bindEvent( this.ptr, "up", function( event ){ self._mouseup( event ); }); this.ptr.find("a") .click(function(){ self.is.clicked = true; if( !self.is.toclick ){ self.is.toclick = true; return false; } }) .mousedown(function( event ){ self._mousedown( event ); return false; }); this.events(); }; Draggable.prototype._mousedown = function( evt ){ this.is.drag = true; this.is.clicked = false; this.is.mouseup = false; var _offset = this.ptr.offset(); var coords = this._getPageCoords( evt ); this.cx = coords.x - _offset.left; this.cy = coords.y - _offset.top; $.extend(this.d, { left: _offset.left, top: _offset.top, width: this.ptr.width(), height: this.ptr.height() }); if( this.outer && this.outer.get(0) ){ this.outer.css({ height: Math.max(this.outer.height(), $(document.body).height()), overflow: "hidden" }); } this.onmousedown( evt ); }; Draggable.prototype._mousemove = function( evt ){ this.is.toclick = false; var coords = this._getPageCoords( evt ); this.onmousemove( evt, coords.x - this.cx, coords.y - this.cy ); }; Draggable.prototype._mouseup = function( evt ){ var oThis = this; if( this.is.drag ){ this.is.drag = false; if( this.outer && this.outer.get(0) ){ if( $.browser.mozilla ){ this.outer.css({ overflow: "hidden" }); } else { this.outer.css({ overflow: "visible" }); } if( $.browser.msie && $.browser.version == '6.0' ){ this.outer.css({ height: "100%" }); } else { this.outer.css({ height: "auto" }); } } this.onmouseup( evt ); } }; window.Draggable = Draggable; })( jQuery ); /** * jquery.slider - Slider ui control in jQuery * * Written by * Egor Khmelev (hmelyoff@gmail.com) * * Licensed under the MIT (MIT-LICENSE.txt). * * @author Egor Khmelev * @version 1.1.0-RELEASE ($Id$) * * Dependencies * * jQuery (http://jquery.com) * jquery.numberformatter (http://code.google.com/p/jquery-numberformatter/) * tmpl (http://ejohn.org/blog/javascript-micro-templating/) * jquery.dependClass * draggable * **/ (function( $ ) { function isArray( value ){ if( typeof value == "undefined" ) return false; if (value instanceof Array || (!(value instanceof Object) && (Object.prototype.toString.call((value)) == '[object Array]') || typeof value.length == 'number' && typeof value.splice != 'undefined' && typeof value.propertyIsEnumerable != 'undefined' && !value.propertyIsEnumerable('splice') )) { return true; } return false; } $.slider = function( node, settings ){ var jNode = $(node); if( !jNode.data( "jslider" ) ) jNode.data( "jslider", new jSlider( node, settings ) ); return jNode.data( "jslider" ); }; $.fn.slider = function( action, opt_value ){ var returnValue, args = arguments; function isDef( val ){ return val !== undefined; }; function isDefAndNotNull( val ){ return val != null; }; this.each(function(){ var self = $.slider( this, action ); // do actions if( typeof action == "string" ){ switch( action ){ case "value": if( isDef( args[ 1 ] ) && isDef( args[ 2 ] ) ){ var pointers = self.getPointers(); if( isDefAndNotNull( pointers[0] ) && isDefAndNotNull( args[1] ) ){ pointers[0].set( args[ 1 ] ); pointers[0].setIndexOver(); } if( isDefAndNotNull( pointers[1] ) && isDefAndNotNull( args[2] ) ){ pointers[1].set( args[ 2 ] ); pointers[1].setIndexOver(); } } else if( isDef( args[ 1 ] ) ){ var pointers = self.getPointers(); if( isDefAndNotNull( pointers[0] ) && isDefAndNotNull( args[1] ) ){ pointers[0].set( args[ 1 ] ); pointers[0].setIndexOver(); } } else returnValue = self.getValue(); break; case "prc": if( isDef( args[ 1 ] ) && isDef( args[ 2 ] ) ){ var pointers = self.getPointers(); if( isDefAndNotNull( pointers[0] ) && isDefAndNotNull( args[1] ) ){ pointers[0]._set( args[ 1 ] ); pointers[0].setIndexOver(); } if( isDefAndNotNull( pointers[1] ) && isDefAndNotNull( args[2] ) ){ pointers[1]._set( args[ 2 ] ); pointers[1].setIndexOver(); } } else if( isDef( args[ 1 ] ) ){ var pointers = self.getPointers(); if( isDefAndNotNull( pointers[0] ) && isDefAndNotNull( args[1] ) ){ pointers[0]._set( args[ 1 ] ); pointers[0].setIndexOver(); } } else returnValue = self.getPrcValue(); break; case "calculatedValue": var value = self.getValue().split(";"); returnValue = ""; for (var i=0; i < value.length; i++) { returnValue += (i > 0 ? ";" : "") + self.nice( value[i] ); }; break; case "skin": self.setSkin( args[1] ); break; }; } // return actual object else if( !action && !opt_value ){ if( !isArray( returnValue ) ) returnValue = []; returnValue.push( self ); } }); // flatten array just with one slider if( isArray( returnValue ) && returnValue.length == 1 ) returnValue = returnValue[ 0 ]; return returnValue || this; }; var OPTIONS = { settings: { from: 1, to: 10, step: 1, smooth: true, limits: true, round: 0, format: { format: "#,##0.##" }, value: "5;7", dimension: "" }, className: "jslider", selector: ".jslider-", template: tmpl( '' + '
' + '
' + '' + '' + '
' + '
' + '
' + '
<%=settings.from%>
' + '
<%=settings.to%><%=settings.dimension%>
' + '
<%=settings.dimension%>
' + '
<%=settings.dimension%>
' + '
<%=scale%>
'+ '
' + '
' ) }; function jSlider(){ return this.init.apply( this, arguments ); }; jSlider.prototype.init = function( node, settings ){ this.settings = $.extend(true, {}, OPTIONS.settings, settings ? settings : {}); // obj.sliderHandler = this; this.inputNode = $( node ).hide(); this.settings.interval = this.settings.to-this.settings.from; this.settings.value = this.inputNode.attr("value"); if( this.settings.calculate && $.isFunction( this.settings.calculate ) ) this.nice = this.settings.calculate; if( this.settings.onstatechange && $.isFunction( this.settings.onstatechange ) ) this.onstatechange = this.settings.onstatechange; this.is = { init: false }; this.o = {}; this.create(); }; jSlider.prototype.onstatechange = function(){ }; jSlider.prototype.create = function(){ var $this = this; this.domNode = $( OPTIONS.template({ className: OPTIONS.className, settings: { from: this.nice( this.settings.from ), to: this.nice( this.settings.to ), dimension: this.settings.dimension }, scale: this.generateScale() }) ); this.inputNode.after( this.domNode ); this.drawScale(); // set skin class if( this.settings.skin && this.settings.skin.length > 0 ) this.setSkin( this.settings.skin ); this.sizes = { domWidth: this.domNode.width(), domOffset: this.domNode.offset() }; // find some objects $.extend(this.o, { pointers: {}, labels: { 0: { o: this.domNode.find(OPTIONS.selector + "value").not(OPTIONS.selector + "value-to") }, 1: { o: this.domNode.find(OPTIONS.selector + "value").filter(OPTIONS.selector + "value-to") } }, limits: { 0: this.domNode.find(OPTIONS.selector + "label").not(OPTIONS.selector + "label-to"), 1: this.domNode.find(OPTIONS.selector + "label").filter(OPTIONS.selector + "label-to") } }); $.extend(this.o.labels[0], { value: this.o.labels[0].o.find("span") }); $.extend(this.o.labels[1], { value: this.o.labels[1].o.find("span") }); if( !$this.settings.value.split(";")[1] ){ this.settings.single = true; this.domNode.addDependClass("single"); } if( !$this.settings.limits ) this.domNode.addDependClass("limitless"); this.domNode.find(OPTIONS.selector + "pointer").each(function( i ){ var value = $this.settings.value.split(";")[i]; if( value ){ $this.o.pointers[i] = new jSliderPointer( this, i, $this ); var prev = $this.settings.value.split(";")[i-1]; if( prev && new Number(value) < new Number(prev) ) value = prev; value = value < $this.settings.from ? $this.settings.from : value; value = value > $this.settings.to ? $this.settings.to : value; $this.o.pointers[i].set( value, true ); } }); this.o.value = this.domNode.find(".v"); this.is.init = true; $.each(this.o.pointers, function(i){ $this.redraw(this); }); (function(self){ $(window).resize(function(){ self.onresize(); }); })(this); }; jSlider.prototype.setSkin = function( skin ){ if( this.skin_ ) this.domNode.removeDependClass( this.skin_, "_" ); this.domNode.addDependClass( this.skin_ = skin, "_" ); }; jSlider.prototype.setPointersIndex = function( i ){ $.each(this.getPointers(), function(i){ this.index( i ); }); }; jSlider.prototype.getPointers = function(){ return this.o.pointers; }; jSlider.prototype.generateScale = function(){ if( this.settings.scale && this.settings.scale.length > 0 ){ var str = ""; var s = this.settings.scale; var prc = Math.round((100/(s.length-1))*10)/10; for( var i=0; i < s.length; i++ ){ str += '' + ( s[i] != '|' ? '' + s[i] + '' : '' ) + ''; }; return str; } else return ""; return ""; }; jSlider.prototype.drawScale = function(){ this.domNode.find(OPTIONS.selector + "scale span ins").each(function(){ $(this).css({ marginLeft: -$(this).outerWidth()/2 }); }); }; jSlider.prototype.onresize = function(){ var self = this; this.sizes = { domWidth: this.domNode.width(), domOffset: this.domNode.offset() }; $.each(this.o.pointers, function(i){ self.redraw(this); }); }; jSlider.prototype.update = function(){ this.onresize(); this.drawScale(); }; jSlider.prototype.limits = function( x, pointer ){ // smooth if( !this.settings.smooth ){ var step = this.settings.step*100 / ( this.settings.interval ); x = Math.round( x/step ) * step; } var another = this.o.pointers[1-pointer.uid]; if( another && pointer.uid && x < another.value.prc ) x = another.value.prc; if( another && !pointer.uid && x > another.value.prc ) x = another.value.prc; // base limit if( x < 0 ) x = 0; if( x > 100 ) x = 100; return Math.round( x*10 ) / 10; }; jSlider.prototype.redraw = function( pointer ){ if( !this.is.init ) return false; this.setValue(); // redraw range line if( this.o.pointers[0] && this.o.pointers[1] ) this.o.value.css({ left: this.o.pointers[0].value.prc + "%", width: ( this.o.pointers[1].value.prc - this.o.pointers[0].value.prc ) + "%" }); this.o.labels[pointer.uid].value.html( this.nice( pointer.value.origin ) ); // redraw position of labels this.redrawLabels( pointer ); }; jSlider.prototype.redrawLabels = function( pointer ){ function setPosition( label, sizes, prc ){ sizes.margin = -sizes.label/2; // left limit label_left = sizes.border + sizes.margin; if( label_left < 0 ) sizes.margin -= label_left; // right limit if( sizes.border+sizes.label / 2 > self.sizes.domWidth ){ sizes.margin = 0; sizes.right = true; } else sizes.right = false; label.o.css({ left: prc + "%", marginLeft: sizes.margin, right: "auto" }); if( sizes.right ) label.o.css({ left: "auto", right: 0 }); return sizes; } var self = this; var label = this.o.labels[pointer.uid]; var prc = pointer.value.prc; this.sizes = { domWidth: this.domNode.width(), domOffset: this.domNode.offset() }; var sizes = { label: label.o.outerWidth(), right: false, border: ( prc * this.sizes.domWidth ) / 100 }; if( !this.settings.single ){ // glue if near; var another = this.o.pointers[1-pointer.uid]; var another_label = this.o.labels[another.uid]; switch( pointer.uid ){ case 0: if( sizes.border+sizes.label / 2 > another_label.o.offset().left-this.sizes.domOffset.left ){ another_label.o.css({ visibility: "hidden" }); another_label.value.html( this.nice( another.value.origin ) ); label.o.css({ visibility: "visible" }); prc = ( another.value.prc - prc ) / 2 + prc; if( another.value.prc != pointer.value.prc ){ label.value.html( this.nice(pointer.value.origin) + " – " + this.nice(another.value.origin) ); sizes.label = label.o.outerWidth(); sizes.border = ( prc * this.sizes.domWidth ) / 100; } } else { another_label.o.css({ visibility: "visible" }); } break; case 1: if( sizes.border - sizes.label / 2 < another_label.o.offset().left - this.sizes.domOffset.left + another_label.o.outerWidth() ){ another_label.o.css({ visibility: "hidden" }); another_label.value.html( this.nice(another.value.origin) ); label.o.css({ visibility: "visible" }); prc = ( prc - another.value.prc ) / 2 + another.value.prc; if( another.value.prc != pointer.value.prc ){ label.value.html( this.nice(another.value.origin) + " – " + this.nice(pointer.value.origin) ); sizes.label = label.o.outerWidth(); sizes.border = ( prc * this.sizes.domWidth ) / 100; } } else { another_label.o.css({ visibility: "visible" }); } break; } } sizes = setPosition( label, sizes, prc ); /* draw second label */ if( another_label ){ var sizes = { label: another_label.o.outerWidth(), right: false, border: ( another.value.prc * this.sizes.domWidth ) / 100 }; sizes = setPosition( another_label, sizes, another.value.prc ); } this.redrawLimits(); }; jSlider.prototype.redrawLimits = function(){ if( this.settings.limits ){ var limits = [ true, true ]; for( key in this.o.pointers ){ if( !this.settings.single || key == 0 ){ var pointer = this.o.pointers[key]; var label = this.o.labels[pointer.uid]; var label_left = label.o.offset().left - this.sizes.domOffset.left; var limit = this.o.limits[0]; if( label_left < limit.outerWidth() ) limits[0] = false; var limit = this.o.limits[1]; if( label_left + label.o.outerWidth() > this.sizes.domWidth - limit.outerWidth() ) limits[1] = false; } }; for( var i=0; i < limits.length; i++ ){ if( limits[i] ) this.o.limits[i].fadeIn("fast"); else this.o.limits[i].fadeOut("fast"); }; } }; jSlider.prototype.setValue = function(){ var value = this.getValue(); this.inputNode.attr( "value", value ); this.onstatechange.call( this, value ); }; jSlider.prototype.getValue = function(){ if(!this.is.init) return false; var $this = this; var value = ""; $.each( this.o.pointers, function(i){ if( this.value.prc != undefined && !isNaN(this.value.prc) ) value += (i > 0 ? ";" : "") + $this.prcToValue( this.value.prc ); }); return value; }; jSlider.prototype.getPrcValue = function(){ if(!this.is.init) return false; var $this = this; var value = ""; $.each( this.o.pointers, function(i){ if( this.value.prc != undefined && !isNaN(this.value.prc) ) value += (i > 0 ? ";" : "") + this.value.prc; }); return value; }; jSlider.prototype.prcToValue = function( prc ){ if( this.settings.heterogeneity && this.settings.heterogeneity.length > 0 ){ var h = this.settings.heterogeneity; var _start = 0; var _from = this.settings.from; for( var i=0; i <= h.length; i++ ){ if( h[i] ) var v = h[i].split("/"); else var v = [100, this.settings.to]; v[0] = new Number(v[0]); v[1] = new Number(v[1]); if( prc >= _start && prc <= v[0] ) { var value = _from + ( (prc-_start) * (v[1]-_from) ) / (v[0]-_start); } _start = v[0]; _from = v[1]; }; } else { var value = this.settings.from + ( prc * this.settings.interval ) / 100; } return this.round( value ); }; jSlider.prototype.valueToPrc = function( value, pointer ){ if( this.settings.heterogeneity && this.settings.heterogeneity.length > 0 ){ var h = this.settings.heterogeneity; var _start = 0; var _from = this.settings.from; for (var i=0; i <= h.length; i++) { if(h[i]) var v = h[i].split("/"); else var v = [100, this.settings.to]; v[0] = new Number(v[0]); v[1] = new Number(v[1]); if(value >= _from && value <= v[1]){ var prc = pointer.limits(_start + (value-_from)*(v[0]-_start)/(v[1]-_from)); } _start = v[0]; _from = v[1]; }; } else { var prc = pointer.limits((value-this.settings.from)*100/this.settings.interval); } return prc; }; jSlider.prototype.round = function( value ){ value = Math.round( value / this.settings.step ) * this.settings.step; if( this.settings.round ) value = Math.round( value * Math.pow(10, this.settings.round) ) / Math.pow(10, this.settings.round); else value = Math.round( value ); return value; }; jSlider.prototype.nice = function( value ){ value = value.toString().replace(/,/gi, ".").replace(/ /gi, "");; if( $.formatNumber ){ return $.formatNumber( new Number(value), this.settings.format || {} ).replace( /-/gi, "−" ); } else { return new Number(value); } }; function jSliderPointer(){ Draggable.apply( this, arguments ); } jSliderPointer.prototype = new Draggable(); jSliderPointer.prototype.oninit = function( ptr, id, _constructor ){ this.uid = id; this.parent = _constructor; this.value = {}; this.settings = this.parent.settings; }; jSliderPointer.prototype.onmousedown = function(evt){ this._parent = { offset: this.parent.domNode.offset(), width: this.parent.domNode.width() }; this.ptr.addDependClass("hover"); this.setIndexOver(); }; jSliderPointer.prototype.onmousemove = function( evt, x ){ var coords = this._getPageCoords( evt ); this._set( this.calc( coords.x ) ); }; jSliderPointer.prototype.onmouseup = function( evt ){ if( this.parent.settings.callback && $.isFunction(this.parent.settings.callback) ) this.parent.settings.callback.call( this.parent, this.parent.getValue() ); this.ptr.removeDependClass("hover"); }; jSliderPointer.prototype.setIndexOver = function(){ this.parent.setPointersIndex( 1 ); this.index( 2 ); }; jSliderPointer.prototype.index = function( i ){ this.ptr.css({ zIndex: i }); }; jSliderPointer.prototype.limits = function( x ){ return this.parent.limits( x, this ); }; jSliderPointer.prototype.calc = function(coords){ var x = this.limits(((coords-this._parent.offset.left)*100)/this._parent.width); return x; }; jSliderPointer.prototype.set = function( value, opt_origin ){ this.value.origin = this.parent.round(value); this._set( this.parent.valueToPrc( value, this ), opt_origin ); }; jSliderPointer.prototype._set = function( prc, opt_origin ){ if( !opt_origin ) this.value.origin = this.parent.prcToValue(prc); this.value.prc = prc; this.ptr.css({ left: prc + "%" }); this.parent.redraw(this); }; })(jQuery); ; /** * @license * Lodash lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE */ ;(function(){function n(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function t(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u"']/g,G=RegExp(V.source),H=RegExp(K.source),J=/<%-([\s\S]+?)%>/g,Y=/<%([\s\S]+?)%>/g,Q=/<%=([\s\S]+?)%>/g,X=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,nn=/^\w*$/,tn=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,rn=/[\\^$.*+?()[\]{}|]/g,en=RegExp(rn.source),un=/^\s+|\s+$/g,on=/^\s+/,fn=/\s+$/,cn=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,an=/\{\n\/\* \[wrapped with (.+)\] \*/,ln=/,? & /,sn=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,hn=/\\(\\)?/g,pn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,_n=/\w*$/,vn=/^[-+]0x[0-9a-f]+$/i,gn=/^0b[01]+$/i,dn=/^\[object .+?Constructor\]$/,yn=/^0o[0-7]+$/i,bn=/^(?:0|[1-9]\d*)$/,xn=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,jn=/($^)/,wn=/['\n\r\u2028\u2029\\]/g,mn="[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?(?:\\u200d(?:[^\\ud800-\\udfff]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff])[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?)*",An="(?:[\\u2700-\\u27bf]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff])"+mn,En="(?:[^\\ud800-\\udfff][\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]?|[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\ud800-\\udfff])",kn=RegExp("['\u2019]","g"),Sn=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g"),On=RegExp("\\ud83c[\\udffb-\\udfff](?=\\ud83c[\\udffb-\\udfff])|"+En+mn,"g"),In=RegExp(["[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+(?:['\u2019](?:d|ll|m|re|s|t|ve))?(?=[\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000]|[A-Z\\xc0-\\xd6\\xd8-\\xde]|$)|(?:[A-Z\\xc0-\\xd6\\xd8-\\xde]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?(?=[\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000]|[A-Z\\xc0-\\xd6\\xd8-\\xde](?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])|$)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?(?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:d|ll|m|re|s|t|ve))?|[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?|\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])|\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])|\\d+",An].join("|"),"g"),Rn=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]"),zn=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Wn="Array Buffer DataView Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Map Math Object Promise RegExp Set String Symbol TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap _ clearTimeout isFinite parseInt setTimeout".split(" "),Bn={}; Bn["[object Float32Array]"]=Bn["[object Float64Array]"]=Bn["[object Int8Array]"]=Bn["[object Int16Array]"]=Bn["[object Int32Array]"]=Bn["[object Uint8Array]"]=Bn["[object Uint8ClampedArray]"]=Bn["[object Uint16Array]"]=Bn["[object Uint32Array]"]=true,Bn["[object Arguments]"]=Bn["[object Array]"]=Bn["[object ArrayBuffer]"]=Bn["[object Boolean]"]=Bn["[object DataView]"]=Bn["[object Date]"]=Bn["[object Error]"]=Bn["[object Function]"]=Bn["[object Map]"]=Bn["[object Number]"]=Bn["[object Object]"]=Bn["[object RegExp]"]=Bn["[object Set]"]=Bn["[object String]"]=Bn["[object WeakMap]"]=false; var Ln={};Ln["[object Arguments]"]=Ln["[object Array]"]=Ln["[object ArrayBuffer]"]=Ln["[object DataView]"]=Ln["[object Boolean]"]=Ln["[object Date]"]=Ln["[object Float32Array]"]=Ln["[object Float64Array]"]=Ln["[object Int8Array]"]=Ln["[object Int16Array]"]=Ln["[object Int32Array]"]=Ln["[object Map]"]=Ln["[object Number]"]=Ln["[object Object]"]=Ln["[object RegExp]"]=Ln["[object Set]"]=Ln["[object String]"]=Ln["[object Symbol]"]=Ln["[object Uint8Array]"]=Ln["[object Uint8ClampedArray]"]=Ln["[object Uint16Array]"]=Ln["[object Uint32Array]"]=true, Ln["[object Error]"]=Ln["[object Function]"]=Ln["[object WeakMap]"]=false;var Un={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},Cn=parseFloat,Dn=parseInt,Mn=typeof global=="object"&&global&&global.Object===Object&&global,Tn=typeof self=="object"&&self&&self.Object===Object&&self,$n=Mn||Tn||Function("return this")(),Fn=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Nn=Fn&&typeof module=="object"&&module&&!module.nodeType&&module,Pn=Nn&&Nn.exports===Fn,Zn=Pn&&Mn.process,qn=function(){ try{var n=Nn&&Nn.f&&Nn.f("util").types;return n?n:Zn&&Zn.binding&&Zn.binding("util")}catch(n){}}(),Vn=qn&&qn.isArrayBuffer,Kn=qn&&qn.isDate,Gn=qn&&qn.isMap,Hn=qn&&qn.isRegExp,Jn=qn&&qn.isSet,Yn=qn&&qn.isTypedArray,Qn=b("length"),Xn=x({"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I", "\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C", "\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i", "\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r", "\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij", "\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"}),nt=x({"&":"&","<":"<",">":">",'"':""","'":"'"}),tt=x({"&":"&","<":"<",">":">",""":'"',"'":"'"}),rt=function x(mn){function An(n){if(yu(n)&&!ff(n)&&!(n instanceof Un)){if(n instanceof On)return n;if(oi.call(n,"__wrapped__"))return Fe(n)}return new On(n)}function En(){}function On(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=T}function Un(n){this.__wrapped__=n, this.__actions__=[],this.__dir__=1,this.__filtered__=false,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}function Mn(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t=t?n:t)),n}function _t(n,t,e,u,i,o){var f,c=1&t,a=2&t,l=4&t;if(e&&(f=i?e(n,u,i,o):e(n)),f!==T)return f;if(!du(n))return n;if(u=ff(n)){if(f=me(n),!c)return Ur(n,f)}else{var s=vo(n),h="[object Function]"==s||"[object GeneratorFunction]"==s;if(af(n))return Ir(n,c);if("[object Object]"==s||"[object Arguments]"==s||h&&!i){if(f=a||h?{}:Ae(n),!c)return a?Mr(n,lt(f,n)):Dr(n,at(f,n))}else{if(!Ln[s])return i?n:{};f=Ee(n,s,c)}}if(o||(o=new Zn), i=o.get(n))return i;o.set(n,f),pf(n)?n.forEach(function(r){f.add(_t(r,t,e,r,n,o))}):sf(n)&&n.forEach(function(r,u){f.set(u,_t(r,t,e,u,n,o))});var a=l?a?ve:_e:a?Bu:Wu,p=u?T:a(n);return r(p||n,function(r,u){p&&(u=r,r=n[u]),ot(f,u,_t(r,t,e,u,n,o))}),f}function vt(n){var t=Wu(n);return function(r){return gt(r,n,t)}}function gt(n,t,r){var e=r.length;if(null==n)return!e;for(n=Qu(n);e--;){var u=r[e],i=t[u],o=n[u];if(o===T&&!(u in n)||!i(o))return false}return true}function dt(n,t,r){if(typeof n!="function")throw new ti("Expected a function"); return bo(function(){n.apply(T,r)},t)}function yt(n,t,r,e){var u=-1,i=o,a=true,l=n.length,s=[],h=t.length;if(!l)return s;r&&(t=c(t,k(r))),e?(i=f,a=false):200<=t.length&&(i=O,a=false,t=new Nn(t));n:for(;++ut}function Rt(n,t){return null!=n&&oi.call(n,t)}function zt(n,t){return null!=n&&t in Qu(n)}function Wt(n,t,r){for(var e=r?f:o,u=n[0].length,i=n.length,a=i,l=Ku(i),s=1/0,h=[];a--;){var p=n[a];a&&t&&(p=c(p,k(t))),s=Ci(p.length,s), l[a]=!r&&(t||120<=u&&120<=p.length)?new Nn(a&&p):T}var p=n[0],_=-1,v=l[0];n:for(;++_r.length?t:kt(t,hr(r,0,-1)),r=null==t?t:t[Me(Ve(r))],null==r?T:n(r,t,e)}function Ut(n){return yu(n)&&"[object Arguments]"==Ot(n)}function Ct(n){ return yu(n)&&"[object ArrayBuffer]"==Ot(n)}function Dt(n){return yu(n)&&"[object Date]"==Ot(n)}function Mt(n,t,r,e,u){if(n===t)t=true;else if(null==n||null==t||!yu(n)&&!yu(t))t=n!==n&&t!==t;else n:{var i=ff(n),o=ff(t),f=i?"[object Array]":vo(n),c=o?"[object Array]":vo(t),f="[object Arguments]"==f?"[object Object]":f,c="[object Arguments]"==c?"[object Object]":c,a="[object Object]"==f,o="[object Object]"==c;if((c=f==c)&&af(n)){if(!af(t)){t=false;break n}i=true,a=false}if(c&&!a)u||(u=new Zn),t=i||_f(n)?se(n,t,r,e,Mt,u):he(n,t,f,r,e,Mt,u);else{ if(!(1&r)&&(i=a&&oi.call(n,"__wrapped__"),f=o&&oi.call(t,"__wrapped__"),i||f)){n=i?n.value():n,t=f?t.value():t,u||(u=new Zn),t=Mt(n,t,r,e,u);break n}if(c)t:if(u||(u=new Zn),i=1&r,f=_e(n),o=f.length,c=_e(t).length,o==c||i){for(a=o;a--;){var l=f[a];if(!(i?l in t:oi.call(t,l))){t=false;break t}}if((c=u.get(n))&&u.get(t))t=c==t;else{c=true,u.set(n,t),u.set(t,n);for(var s=i;++at?r:0,Se(t,r)?n[t]:T}function Xt(n,t,r){var e=-1;return t=c(t.length?t:[$u],k(ye())),n=Gt(n,function(n){return{ a:c(t,function(t){return t(n)}),b:++e,c:n}}),w(n,function(n,t){var e;n:{e=-1;for(var u=n.a,i=t.a,o=u.length,f=r.length;++e=f?c:c*("desc"==r[e]?-1:1);break n}}e=n.b-t.b}return e})}function nr(n,t){return tr(n,t,function(t,r){return zu(n,r)})}function tr(n,t,r){for(var e=-1,u=t.length,i={};++et||9007199254740991t&&(t=-t>u?0:u+t),r=r>u?u:r,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=Ku(u);++e=u){for(;e>>1,o=n[i];null!==o&&!wu(o)&&(r?o<=t:ot.length?n:kt(n,hr(t,0,-1)),null==n||delete n[Me(Ve(t))]}function jr(n,t,r,e){for(var u=n.length,i=e?u:-1;(e?i--:++ie)return e?br(n[0]):[];for(var u=-1,i=Ku(e);++u=e?n:hr(n,t,r)}function Ir(n,t){if(t)return n.slice();var r=n.length,r=gi?gi(r):new n.constructor(r);return n.copy(r),r}function Rr(n){var t=new n.constructor(n.byteLength);return new vi(t).set(new vi(n)), t}function zr(n,t){return new n.constructor(t?Rr(n.buffer):n.buffer,n.byteOffset,n.length)}function Wr(n,t){if(n!==t){var r=n!==T,e=null===n,u=n===n,i=wu(n),o=t!==T,f=null===t,c=t===t,a=wu(t);if(!f&&!a&&!i&&n>t||i&&o&&c&&!f&&!a||e&&o&&c||!r&&c||!u)return 1;if(!e&&!i&&!a&&nu?T:i,u=1),t=Qu(t);++eo&&f[0]!==a&&f[o-1]!==a?[]:L(f,a), o-=c.length,or?r?or(t,n):t:(r=or(t,Oi(n/D(t))),Rn.test(t)?Or(M(r),0,n).join(""):r.slice(0,n))}function te(t,r,e,u){function i(){for(var r=-1,c=arguments.length,a=-1,l=u.length,s=Ku(l+c),h=this&&this!==$n&&this instanceof i?f:t;++at||e)&&(1&n&&(i[2]=h[2],t|=1&r?0:4),(r=h[3])&&(e=i[3],i[3]=e?Br(e,r,h[4]):r,i[4]=e?L(i[3],"__lodash_placeholder__"):h[4]),(r=h[5])&&(e=i[5],i[5]=e?Lr(e,r,h[6]):r,i[6]=e?L(i[5],"__lodash_placeholder__"):h[6]),(r=h[7])&&(i[7]=r),128&n&&(i[8]=null==i[8]?h[8]:Ci(i[8],h[8])),null==i[9]&&(i[9]=h[9]),i[0]=h[0],i[1]=t),n=i[0], t=i[1],r=i[2],e=i[3],u=i[4],f=i[9]=i[9]===T?c?0:n.length:Ui(i[9]-a,0),!f&&24&t&&(t&=-25),Ue((h?co:yo)(t&&1!=t?8==t||16==t?Kr(n,t,f):32!=t&&33!=t||u.length?Jr.apply(T,i):te(n,t,r,e):Pr(n,t,r),i),n,t)}function ce(n,t,r,e){return n===T||lu(n,ei[r])&&!oi.call(e,r)?t:n}function ae(n,t,r,e,u,i){return du(n)&&du(t)&&(i.set(t,n),Yt(n,t,T,ae,i),i.delete(t)),n}function le(n){return xu(n)?T:n}function se(n,t,r,e,u,i){var o=1&r,f=n.length,c=t.length;if(f!=c&&!(o&&c>f))return false;if((c=i.get(n))&&i.get(t))return c==t; var c=-1,a=true,l=2&r?new Nn:T;for(i.set(n,t),i.set(t,n);++cr&&(r=Ui(e+r,0)),_(n,ye(t,3),r)):-1}function Pe(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e-1;return r!==T&&(u=Eu(r),u=0>r?Ui(e+u,0):Ci(u,e-1)), _(n,ye(t,3),u,true)}function Ze(n){return(null==n?0:n.length)?wt(n,1):[]}function qe(n){return n&&n.length?n[0]:T}function Ve(n){var t=null==n?0:n.length;return t?n[t-1]:T}function Ke(n,t){return n&&n.length&&t&&t.length?er(n,t):n}function Ge(n){return null==n?n:$i.call(n)}function He(n){if(!n||!n.length)return[];var t=0;return n=i(n,function(n){if(hu(n))return t=Ui(n.length,t),true}),A(t,function(t){return c(n,b(t))})}function Je(t,r){if(!t||!t.length)return[];var e=He(t);return null==r?e:c(e,function(t){ return n(r,T,t)})}function Ye(n){return n=An(n),n.__chain__=true,n}function Qe(n,t){return t(n)}function Xe(){return this}function nu(n,t){return(ff(n)?r:uo)(n,ye(t,3))}function tu(n,t){return(ff(n)?e:io)(n,ye(t,3))}function ru(n,t){return(ff(n)?c:Gt)(n,ye(t,3))}function eu(n,t,r){return t=r?T:t,t=n&&null==t?n.length:t,fe(n,128,T,T,T,T,t)}function uu(n,t){var r;if(typeof t!="function")throw new ti("Expected a function");return n=Eu(n),function(){return 0<--n&&(r=t.apply(this,arguments)),1>=n&&(t=T), r}}function iu(n,t,r){return t=r?T:t,n=fe(n,8,T,T,T,T,T,t),n.placeholder=iu.placeholder,n}function ou(n,t,r){return t=r?T:t,n=fe(n,16,T,T,T,T,T,t),n.placeholder=ou.placeholder,n}function fu(n,t,r){function e(t){var r=c,e=a;return c=a=T,_=t,s=n.apply(e,r)}function u(n){var r=n-p;return n-=_,p===T||r>=t||0>r||g&&n>=l}function i(){var n=Go();if(u(n))return o(n);var r,e=bo;r=n-_,n=t-(n-p),r=g?Ci(n,l-r):n,h=e(i,r)}function o(n){return h=T,d&&c?e(n):(c=a=T,s)}function f(){var n=Go(),r=u(n);if(c=arguments, a=this,p=n,r){if(h===T)return _=n=p,h=bo(i,t),v?e(n):s;if(g)return lo(h),h=bo(i,t),e(p)}return h===T&&(h=bo(i,t)),s}var c,a,l,s,h,p,_=0,v=false,g=false,d=true;if(typeof n!="function")throw new ti("Expected a function");return t=Su(t)||0,du(r)&&(v=!!r.leading,l=(g="maxWait"in r)?Ui(Su(r.maxWait)||0,t):l,d="trailing"in r?!!r.trailing:d),f.cancel=function(){h!==T&&lo(h),_=0,c=p=a=h=T},f.flush=function(){return h===T?s:o(Go())},f}function cu(n,t){function r(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache; return i.has(u)?i.get(u):(e=n.apply(this,e),r.cache=i.set(u,e)||i,e)}if(typeof n!="function"||null!=t&&typeof t!="function")throw new ti("Expected a function");return r.cache=new(cu.Cache||Fn),r}function au(n){if(typeof n!="function")throw new ti("Expected a function");return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}function lu(n,t){return n===t||n!==n&&t!==t; }function su(n){return null!=n&&gu(n.length)&&!_u(n)}function hu(n){return yu(n)&&su(n)}function pu(n){if(!yu(n))return false;var t=Ot(n);return"[object Error]"==t||"[object DOMException]"==t||typeof n.message=="string"&&typeof n.name=="string"&&!xu(n)}function _u(n){return!!du(n)&&(n=Ot(n),"[object Function]"==n||"[object GeneratorFunction]"==n||"[object AsyncFunction]"==n||"[object Proxy]"==n)}function vu(n){return typeof n=="number"&&n==Eu(n)}function gu(n){return typeof n=="number"&&-1=n; }function du(n){var t=typeof n;return null!=n&&("object"==t||"function"==t)}function yu(n){return null!=n&&typeof n=="object"}function bu(n){return typeof n=="number"||yu(n)&&"[object Number]"==Ot(n)}function xu(n){return!(!yu(n)||"[object Object]"!=Ot(n))&&(n=di(n),null===n||(n=oi.call(n,"constructor")&&n.constructor,typeof n=="function"&&n instanceof n&&ii.call(n)==li))}function ju(n){return typeof n=="string"||!ff(n)&&yu(n)&&"[object String]"==Ot(n)}function wu(n){return typeof n=="symbol"||yu(n)&&"[object Symbol]"==Ot(n); }function mu(n){if(!n)return[];if(su(n))return ju(n)?M(n):Ur(n);if(wi&&n[wi]){n=n[wi]();for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}return t=vo(n),("[object Map]"==t?W:"[object Set]"==t?U:Uu)(n)}function Au(n){return n?(n=Su(n),n===$||n===-$?1.7976931348623157e308*(0>n?-1:1):n===n?n:0):0===n?n:0}function Eu(n){n=Au(n);var t=n%1;return n===n?t?n-t:n:0}function ku(n){return n?pt(Eu(n),0,4294967295):0}function Su(n){if(typeof n=="number")return n;if(wu(n))return F;if(du(n)&&(n=typeof n.valueOf=="function"?n.valueOf():n, n=du(n)?n+"":n),typeof n!="string")return 0===n?n:+n;n=n.replace(un,"");var t=gn.test(n);return t||yn.test(n)?Dn(n.slice(2),t?2:8):vn.test(n)?F:+n}function Ou(n){return Cr(n,Bu(n))}function Iu(n){return null==n?"":yr(n)}function Ru(n,t,r){return n=null==n?T:kt(n,t),n===T?r:n}function zu(n,t){return null!=n&&we(n,t,zt)}function Wu(n){return su(n)?qn(n):Vt(n)}function Bu(n){if(su(n))n=qn(n,true);else if(du(n)){var t,r=ze(n),e=[];for(t in n)("constructor"!=t||!r&&oi.call(n,t))&&e.push(t);n=e}else{if(t=[], null!=n)for(r in Qu(n))t.push(r);n=t}return n}function Lu(n,t){if(null==n)return{};var r=c(ve(n),function(n){return[n]});return t=ye(t),tr(n,r,function(n,r){return t(n,r[0])})}function Uu(n){return null==n?[]:S(n,Wu(n))}function Cu(n){return $f(Iu(n).toLowerCase())}function Du(n){return(n=Iu(n))&&n.replace(xn,Xn).replace(Sn,"")}function Mu(n,t,r){return n=Iu(n),t=r?T:t,t===T?zn.test(n)?n.match(In)||[]:n.match(sn)||[]:n.match(t)||[]}function Tu(n){return function(){return n}}function $u(n){return n; }function Fu(n){return qt(typeof n=="function"?n:_t(n,1))}function Nu(n,t,e){var u=Wu(t),i=Et(t,u);null!=e||du(t)&&(i.length||!u.length)||(e=t,t=n,n=this,i=Et(t,Wu(t)));var o=!(du(e)&&"chain"in e&&!e.chain),f=_u(n);return r(i,function(r){var e=t[r];n[r]=e,f&&(n.prototype[r]=function(){var t=this.__chain__;if(o||t){var r=n(this.__wrapped__);return(r.__actions__=Ur(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,a([this.value()],arguments))})}),n}function Pu(){} function Zu(n){return Ie(n)?b(Me(n)):rr(n)}function qu(){return[]}function Vu(){return false}mn=null==mn?$n:rt.defaults($n.Object(),mn,rt.pick($n,Wn));var Ku=mn.Array,Gu=mn.Date,Hu=mn.Error,Ju=mn.Function,Yu=mn.Math,Qu=mn.Object,Xu=mn.RegExp,ni=mn.String,ti=mn.TypeError,ri=Ku.prototype,ei=Qu.prototype,ui=mn["__core-js_shared__"],ii=Ju.prototype.toString,oi=ei.hasOwnProperty,fi=0,ci=function(){var n=/[^.]+$/.exec(ui&&ui.keys&&ui.keys.IE_PROTO||"");return n?"Symbol(src)_1."+n:""}(),ai=ei.toString,li=ii.call(Qu),si=$n._,hi=Xu("^"+ii.call(oi).replace(rn,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),pi=Pn?mn.Buffer:T,_i=mn.Symbol,vi=mn.Uint8Array,gi=pi?pi.g:T,di=B(Qu.getPrototypeOf,Qu),yi=Qu.create,bi=ei.propertyIsEnumerable,xi=ri.splice,ji=_i?_i.isConcatSpreadable:T,wi=_i?_i.iterator:T,mi=_i?_i.toStringTag:T,Ai=function(){ try{var n=je(Qu,"defineProperty");return n({},"",{}),n}catch(n){}}(),Ei=mn.clearTimeout!==$n.clearTimeout&&mn.clearTimeout,ki=Gu&&Gu.now!==$n.Date.now&&Gu.now,Si=mn.setTimeout!==$n.setTimeout&&mn.setTimeout,Oi=Yu.ceil,Ii=Yu.floor,Ri=Qu.getOwnPropertySymbols,zi=pi?pi.isBuffer:T,Wi=mn.isFinite,Bi=ri.join,Li=B(Qu.keys,Qu),Ui=Yu.max,Ci=Yu.min,Di=Gu.now,Mi=mn.parseInt,Ti=Yu.random,$i=ri.reverse,Fi=je(mn,"DataView"),Ni=je(mn,"Map"),Pi=je(mn,"Promise"),Zi=je(mn,"Set"),qi=je(mn,"WeakMap"),Vi=je(Qu,"create"),Ki=qi&&new qi,Gi={},Hi=Te(Fi),Ji=Te(Ni),Yi=Te(Pi),Qi=Te(Zi),Xi=Te(qi),no=_i?_i.prototype:T,to=no?no.valueOf:T,ro=no?no.toString:T,eo=function(){ function n(){}return function(t){return du(t)?yi?yi(t):(n.prototype=t,t=new n,n.prototype=T,t):{}}}();An.templateSettings={escape:J,evaluate:Y,interpolate:Q,variable:"",imports:{_:An}},An.prototype=En.prototype,An.prototype.constructor=An,On.prototype=eo(En.prototype),On.prototype.constructor=On,Un.prototype=eo(En.prototype),Un.prototype.constructor=Un,Mn.prototype.clear=function(){this.__data__=Vi?Vi(null):{},this.size=0},Mn.prototype.delete=function(n){return n=this.has(n)&&delete this.__data__[n], this.size-=n?1:0,n},Mn.prototype.get=function(n){var t=this.__data__;return Vi?(n=t[n],"__lodash_hash_undefined__"===n?T:n):oi.call(t,n)?t[n]:T},Mn.prototype.has=function(n){var t=this.__data__;return Vi?t[n]!==T:oi.call(t,n)},Mn.prototype.set=function(n,t){var r=this.__data__;return this.size+=this.has(n)?0:1,r[n]=Vi&&t===T?"__lodash_hash_undefined__":t,this},Tn.prototype.clear=function(){this.__data__=[],this.size=0},Tn.prototype.delete=function(n){var t=this.__data__;return n=ft(t,n),!(0>n)&&(n==t.length-1?t.pop():xi.call(t,n,1), --this.size,true)},Tn.prototype.get=function(n){var t=this.__data__;return n=ft(t,n),0>n?T:t[n][1]},Tn.prototype.has=function(n){return-1e?(++this.size,r.push([n,t])):r[e][1]=t,this},Fn.prototype.clear=function(){this.size=0,this.__data__={hash:new Mn,map:new(Ni||Tn),string:new Mn}},Fn.prototype.delete=function(n){return n=be(this,n).delete(n),this.size-=n?1:0,n},Fn.prototype.get=function(n){return be(this,n).get(n); },Fn.prototype.has=function(n){return be(this,n).has(n)},Fn.prototype.set=function(n,t){var r=be(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this},Nn.prototype.add=Nn.prototype.push=function(n){return this.__data__.set(n,"__lodash_hash_undefined__"),this},Nn.prototype.has=function(n){return this.__data__.has(n)},Zn.prototype.clear=function(){this.__data__=new Tn,this.size=0},Zn.prototype.delete=function(n){var t=this.__data__;return n=t.delete(n),this.size=t.size,n},Zn.prototype.get=function(n){ return this.__data__.get(n)},Zn.prototype.has=function(n){return this.__data__.has(n)},Zn.prototype.set=function(n,t){var r=this.__data__;if(r instanceof Tn){var e=r.__data__;if(!Ni||199>e.length)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new Fn(e)}return r.set(n,t),this.size=r.size,this};var uo=Fr(mt),io=Fr(At,true),oo=Nr(),fo=Nr(true),co=Ki?function(n,t){return Ki.set(n,t),n}:$u,ao=Ai?function(n,t){return Ai(n,"toString",{configurable:true,enumerable:false,value:Tu(t),writable:true})}:$u,lo=Ei||function(n){ return $n.clearTimeout(n)},so=Zi&&1/U(new Zi([,-0]))[1]==$?function(n){return new Zi(n)}:Pu,ho=Ki?function(n){return Ki.get(n)}:Pu,po=Ri?function(n){return null==n?[]:(n=Qu(n),i(Ri(n),function(t){return bi.call(n,t)}))}:qu,_o=Ri?function(n){for(var t=[];n;)a(t,po(n)),n=di(n);return t}:qu,vo=Ot;(Fi&&"[object DataView]"!=vo(new Fi(new ArrayBuffer(1)))||Ni&&"[object Map]"!=vo(new Ni)||Pi&&"[object Promise]"!=vo(Pi.resolve())||Zi&&"[object Set]"!=vo(new Zi)||qi&&"[object WeakMap]"!=vo(new qi))&&(vo=function(n){ var t=Ot(n);if(n=(n="[object Object]"==t?n.constructor:T)?Te(n):"")switch(n){case Hi:return"[object DataView]";case Ji:return"[object Map]";case Yi:return"[object Promise]";case Qi:return"[object Set]";case Xi:return"[object WeakMap]"}return t});var go=ui?_u:Vu,yo=Ce(co),bo=Si||function(n,t){return $n.setTimeout(n,t)},xo=Ce(ao),jo=function(n){n=cu(n,function(n){return 500===t.size&&t.clear(),n});var t=n.cache;return n}(function(n){var t=[];return 46===n.charCodeAt(0)&&t.push(""),n.replace(tn,function(n,r,e,u){ t.push(e?u.replace(hn,"$1"):r||n)}),t}),wo=fr(function(n,t){return hu(n)?yt(n,wt(t,1,hu,true)):[]}),mo=fr(function(n,t){var r=Ve(t);return hu(r)&&(r=T),hu(n)?yt(n,wt(t,1,hu,true),ye(r,2)):[]}),Ao=fr(function(n,t){var r=Ve(t);return hu(r)&&(r=T),hu(n)?yt(n,wt(t,1,hu,true),T,r):[]}),Eo=fr(function(n){var t=c(n,Er);return t.length&&t[0]===n[0]?Wt(t):[]}),ko=fr(function(n){var t=Ve(n),r=c(n,Er);return t===Ve(r)?t=T:r.pop(),r.length&&r[0]===n[0]?Wt(r,ye(t,2)):[]}),So=fr(function(n){var t=Ve(n),r=c(n,Er);return(t=typeof t=="function"?t:T)&&r.pop(), r.length&&r[0]===n[0]?Wt(r,T,t):[]}),Oo=fr(Ke),Io=pe(function(n,t){var r=null==n?0:n.length,e=ht(n,t);return ur(n,c(t,function(n){return Se(n,r)?+n:n}).sort(Wr)),e}),Ro=fr(function(n){return br(wt(n,1,hu,true))}),zo=fr(function(n){var t=Ve(n);return hu(t)&&(t=T),br(wt(n,1,hu,true),ye(t,2))}),Wo=fr(function(n){var t=Ve(n),t=typeof t=="function"?t:T;return br(wt(n,1,hu,true),T,t)}),Bo=fr(function(n,t){return hu(n)?yt(n,t):[]}),Lo=fr(function(n){return mr(i(n,hu))}),Uo=fr(function(n){var t=Ve(n);return hu(t)&&(t=T), mr(i(n,hu),ye(t,2))}),Co=fr(function(n){var t=Ve(n),t=typeof t=="function"?t:T;return mr(i(n,hu),T,t)}),Do=fr(He),Mo=fr(function(n){var t=n.length,t=1=t}),of=Ut(function(){return arguments}())?Ut:function(n){return yu(n)&&oi.call(n,"callee")&&!bi.call(n,"callee")},ff=Ku.isArray,cf=Vn?k(Vn):Ct,af=zi||Vu,lf=Kn?k(Kn):Dt,sf=Gn?k(Gn):Tt,hf=Hn?k(Hn):Nt,pf=Jn?k(Jn):Pt,_f=Yn?k(Yn):Zt,vf=ee(Kt),gf=ee(function(n,t){return n<=t}),df=$r(function(n,t){ if(ze(t)||su(t))Cr(t,Wu(t),n);else for(var r in t)oi.call(t,r)&&ot(n,r,t[r])}),yf=$r(function(n,t){Cr(t,Bu(t),n)}),bf=$r(function(n,t,r,e){Cr(t,Bu(t),n,e)}),xf=$r(function(n,t,r,e){Cr(t,Wu(t),n,e)}),jf=pe(ht),wf=fr(function(n,t){n=Qu(n);var r=-1,e=t.length,u=2--n)return t.apply(this,arguments)}},An.ary=eu,An.assign=df,An.assignIn=yf,An.assignInWith=bf,An.assignWith=xf,An.at=jf,An.before=uu,An.bind=Ho,An.bindAll=Nf,An.bindKey=Jo,An.castArray=function(){if(!arguments.length)return[];var n=arguments[0];return ff(n)?n:[n]},An.chain=Ye,An.chunk=function(n,t,r){if(t=(r?Oe(n,t,r):t===T)?1:Ui(Eu(t),0),r=null==n?0:n.length,!r||1>t)return[];for(var e=0,u=0,i=Ku(Oi(r/t));et?0:t,e)):[]},An.dropRight=function(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===T?1:Eu(t),t=e-t,hr(n,0,0>t?0:t)):[]},An.dropRightWhile=function(n,t){return n&&n.length?jr(n,ye(t,3),true,true):[]; },An.dropWhile=function(n,t){return n&&n.length?jr(n,ye(t,3),true):[]},An.fill=function(n,t,r,e){var u=null==n?0:n.length;if(!u)return[];for(r&&typeof r!="number"&&Oe(n,t,r)&&(r=0,e=u),u=n.length,r=Eu(r),0>r&&(r=-r>u?0:u+r),e=e===T||e>u?u:Eu(e),0>e&&(e+=u),e=r>e?0:ku(e);r>>0,r?(n=Iu(n))&&(typeof t=="string"||null!=t&&!hf(t))&&(t=yr(t),!t&&Rn.test(n))?Or(M(n),0,r):n.split(t,r):[]},An.spread=function(t,r){if(typeof t!="function")throw new ti("Expected a function");return r=null==r?0:Ui(Eu(r),0), fr(function(e){var u=e[r];return e=Or(e,0,r),u&&a(e,u),n(t,this,e)})},An.tail=function(n){var t=null==n?0:n.length;return t?hr(n,1,t):[]},An.take=function(n,t,r){return n&&n.length?(t=r||t===T?1:Eu(t),hr(n,0,0>t?0:t)):[]},An.takeRight=function(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===T?1:Eu(t),t=e-t,hr(n,0>t?0:t,e)):[]},An.takeRightWhile=function(n,t){return n&&n.length?jr(n,ye(t,3),false,true):[]},An.takeWhile=function(n,t){return n&&n.length?jr(n,ye(t,3)):[]},An.tap=function(n,t){return t(n), n},An.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new ti("Expected a function");return du(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),fu(n,t,{leading:e,maxWait:t,trailing:u})},An.thru=Qe,An.toArray=mu,An.toPairs=zf,An.toPairsIn=Wf,An.toPath=function(n){return ff(n)?c(n,Me):wu(n)?[n]:Ur(jo(Iu(n)))},An.toPlainObject=Ou,An.transform=function(n,t,e){var u=ff(n),i=u||af(n)||_f(n);if(t=ye(t,4),null==e){var o=n&&n.constructor;e=i?u?new o:[]:du(n)&&_u(o)?eo(di(n)):{}; }return(i?r:mt)(n,function(n,r,u){return t(e,n,r,u)}),e},An.unary=function(n){return eu(n,1)},An.union=Ro,An.unionBy=zo,An.unionWith=Wo,An.uniq=function(n){return n&&n.length?br(n):[]},An.uniqBy=function(n,t){return n&&n.length?br(n,ye(t,2)):[]},An.uniqWith=function(n,t){return t=typeof t=="function"?t:T,n&&n.length?br(n,T,t):[]},An.unset=function(n,t){return null==n||xr(n,t)},An.unzip=He,An.unzipWith=Je,An.update=function(n,t,r){return null==n?n:lr(n,t,kr(r)(kt(n,t)),void 0)},An.updateWith=function(n,t,r,e){ return e=typeof e=="function"?e:T,null!=n&&(n=lr(n,t,kr(r)(kt(n,t)),e)),n},An.values=Uu,An.valuesIn=function(n){return null==n?[]:S(n,Bu(n))},An.without=Bo,An.words=Mu,An.wrap=function(n,t){return nf(kr(t),n)},An.xor=Lo,An.xorBy=Uo,An.xorWith=Co,An.zip=Do,An.zipObject=function(n,t){return Ar(n||[],t||[],ot)},An.zipObjectDeep=function(n,t){return Ar(n||[],t||[],lr)},An.zipWith=Mo,An.entries=zf,An.entriesIn=Wf,An.extend=yf,An.extendWith=bf,Nu(An,An),An.add=Qf,An.attempt=Ff,An.camelCase=Bf,An.capitalize=Cu, An.ceil=Xf,An.clamp=function(n,t,r){return r===T&&(r=t,t=T),r!==T&&(r=Su(r),r=r===r?r:0),t!==T&&(t=Su(t),t=t===t?t:0),pt(Su(n),t,r)},An.clone=function(n){return _t(n,4)},An.cloneDeep=function(n){return _t(n,5)},An.cloneDeepWith=function(n,t){return t=typeof t=="function"?t:T,_t(n,5,t)},An.cloneWith=function(n,t){return t=typeof t=="function"?t:T,_t(n,4,t)},An.conformsTo=function(n,t){return null==t||gt(n,t,Wu(t))},An.deburr=Du,An.defaultTo=function(n,t){return null==n||n!==n?t:n},An.divide=nc,An.endsWith=function(n,t,r){ n=Iu(n),t=yr(t);var e=n.length,e=r=r===T?e:pt(Eu(r),0,e);return r-=t.length,0<=r&&n.slice(r,e)==t},An.eq=lu,An.escape=function(n){return(n=Iu(n))&&H.test(n)?n.replace(K,nt):n},An.escapeRegExp=function(n){return(n=Iu(n))&&en.test(n)?n.replace(rn,"\\$&"):n},An.every=function(n,t,r){var e=ff(n)?u:bt;return r&&Oe(n,t,r)&&(t=T),e(n,ye(t,3))},An.find=Fo,An.findIndex=Ne,An.findKey=function(n,t){return p(n,ye(t,3),mt)},An.findLast=No,An.findLastIndex=Pe,An.findLastKey=function(n,t){return p(n,ye(t,3),At); },An.floor=tc,An.forEach=nu,An.forEachRight=tu,An.forIn=function(n,t){return null==n?n:oo(n,ye(t,3),Bu)},An.forInRight=function(n,t){return null==n?n:fo(n,ye(t,3),Bu)},An.forOwn=function(n,t){return n&&mt(n,ye(t,3))},An.forOwnRight=function(n,t){return n&&At(n,ye(t,3))},An.get=Ru,An.gt=ef,An.gte=uf,An.has=function(n,t){return null!=n&&we(n,t,Rt)},An.hasIn=zu,An.head=qe,An.identity=$u,An.includes=function(n,t,r,e){return n=su(n)?n:Uu(n),r=r&&!e?Eu(r):0,e=n.length,0>r&&(r=Ui(e+r,0)),ju(n)?r<=e&&-1r&&(r=Ui(e+r,0)),v(n,t,r)):-1},An.inRange=function(n,t,r){return t=Au(t),r===T?(r=t,t=0):r=Au(r),n=Su(n),n>=Ci(t,r)&&n=n},An.isSet=pf,An.isString=ju,An.isSymbol=wu,An.isTypedArray=_f,An.isUndefined=function(n){return n===T},An.isWeakMap=function(n){return yu(n)&&"[object WeakMap]"==vo(n)},An.isWeakSet=function(n){return yu(n)&&"[object WeakSet]"==Ot(n)},An.join=function(n,t){return null==n?"":Bi.call(n,t)},An.kebabCase=Lf,An.last=Ve,An.lastIndexOf=function(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e;if(r!==T&&(u=Eu(r),u=0>u?Ui(e+u,0):Ci(u,e-1)), t===t){for(r=u+1;r--&&n[r]!==t;);n=r}else n=_(n,d,u,true);return n},An.lowerCase=Uf,An.lowerFirst=Cf,An.lt=vf,An.lte=gf,An.max=function(n){return n&&n.length?xt(n,$u,It):T},An.maxBy=function(n,t){return n&&n.length?xt(n,ye(t,2),It):T},An.mean=function(n){return y(n,$u)},An.meanBy=function(n,t){return y(n,ye(t,2))},An.min=function(n){return n&&n.length?xt(n,$u,Kt):T},An.minBy=function(n,t){return n&&n.length?xt(n,ye(t,2),Kt):T},An.stubArray=qu,An.stubFalse=Vu,An.stubObject=function(){return{}},An.stubString=function(){ return""},An.stubTrue=function(){return true},An.multiply=rc,An.nth=function(n,t){return n&&n.length?Qt(n,Eu(t)):T},An.noConflict=function(){return $n._===this&&($n._=si),this},An.noop=Pu,An.now=Go,An.pad=function(n,t,r){n=Iu(n);var e=(t=Eu(t))?D(n):0;return!t||e>=t?n:(t=(t-e)/2,ne(Ii(t),r)+n+ne(Oi(t),r))},An.padEnd=function(n,t,r){n=Iu(n);var e=(t=Eu(t))?D(n):0;return t&&et){var e=n;n=t,t=e}return r||n%1||t%1?(r=Ti(),Ci(n+r*(t-n+Cn("1e-"+((r+"").length-1))),t)):ir(n,t)},An.reduce=function(n,t,r){var e=ff(n)?l:j,u=3>arguments.length;return e(n,ye(t,4),r,u,uo)},An.reduceRight=function(n,t,r){var e=ff(n)?s:j,u=3>arguments.length; return e(n,ye(t,4),r,u,io)},An.repeat=function(n,t,r){return t=(r?Oe(n,t,r):t===T)?1:Eu(t),or(Iu(n),t)},An.replace=function(){var n=arguments,t=Iu(n[0]);return 3>n.length?t:t.replace(n[1],n[2])},An.result=function(n,t,r){t=Sr(t,n);var e=-1,u=t.length;for(u||(u=1,n=T);++en||9007199254740991=i)return n;if(i=r-D(e),1>i)return e;if(r=o?Or(o,0,i).join(""):n.slice(0,i),u===T)return r+e;if(o&&(i+=r.length-i),hf(u)){if(n.slice(i).search(u)){ var f=r;for(u.global||(u=Xu(u.source,Iu(_n.exec(u))+"g")),u.lastIndex=0;o=u.exec(f);)var c=o.index;r=r.slice(0,c===T?i:c)}}else n.indexOf(yr(u),i)!=i&&(u=r.lastIndexOf(u),-1e.__dir__?"Right":"")}),e},Un.prototype[n+"Right"]=function(t){return this.reverse()[n](t).reverse()}}),r(["filter","map","takeWhile"],function(n,t){ var r=t+1,e=1==r||3==r;Un.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:ye(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),r(["head","last"],function(n,t){var r="take"+(t?"Right":"");Un.prototype[n]=function(){return this[r](1).value()[0]}}),r(["initial","tail"],function(n,t){var r="drop"+(t?"":"Right");Un.prototype[n]=function(){return this.__filtered__?new Un(this):this[r](1)}}),Un.prototype.compact=function(){return this.filter($u)},Un.prototype.find=function(n){ return this.filter(n).head()},Un.prototype.findLast=function(n){return this.reverse().find(n)},Un.prototype.invokeMap=fr(function(n,t){return typeof n=="function"?new Un(this):this.map(function(r){return Lt(r,n,t)})}),Un.prototype.reject=function(n){return this.filter(au(ye(n)))},Un.prototype.slice=function(n,t){n=Eu(n);var r=this;return r.__filtered__&&(0t)?new Un(r):(0>n?r=r.takeRight(-n):n&&(r=r.drop(n)),t!==T&&(t=Eu(t),r=0>t?r.dropRight(-t):r.take(t-n)),r)},Un.prototype.takeRightWhile=function(n){ return this.reverse().takeWhile(n).reverse()},Un.prototype.toArray=function(){return this.take(4294967295)},mt(Un.prototype,function(n,t){var r=/^(?:filter|find|map|reject)|While$/.test(t),e=/^(?:head|last)$/.test(t),u=An[e?"take"+("last"==t?"Right":""):t],i=e||/^find/.test(t);u&&(An.prototype[t]=function(){function t(n){return n=u.apply(An,a([n],f)),e&&h?n[0]:n}var o=this.__wrapped__,f=e?[1]:arguments,c=o instanceof Un,l=f[0],s=c||ff(o);s&&r&&typeof l=="function"&&1!=l.length&&(c=s=false);var h=this.__chain__,p=!!this.__actions__.length,l=i&&!h,c=c&&!p; return!i&&s?(o=c?o:new Un(this),o=n.apply(o,f),o.__actions__.push({func:Qe,args:[t],thisArg:T}),new On(o,h)):l&&c?n.apply(this,f):(o=this.thru(t),l?e?o.value()[0]:o.value():o)})}),r("pop push shift sort splice unshift".split(" "),function(n){var t=ri[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);An.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){var u=this.value();return t.apply(ff(u)?u:[],n)}return this[r](function(r){return t.apply(ff(r)?r:[],n)}); }}),mt(Un.prototype,function(n,t){var r=An[t];if(r){var e=r.name+"";oi.call(Gi,e)||(Gi[e]=[]),Gi[e].push({name:t,func:r})}}),Gi[Jr(T,2).name]=[{name:"wrapper",func:T}],Un.prototype.clone=function(){var n=new Un(this.__wrapped__);return n.__actions__=Ur(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=Ur(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=Ur(this.__views__),n},Un.prototype.reverse=function(){if(this.__filtered__){var n=new Un(this); n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},Un.prototype.value=function(){var n,t=this.__wrapped__.value(),r=this.__dir__,e=ff(t),u=0>r,i=e?t.length:0;n=i;for(var o=this.__views__,f=0,c=-1,a=o.length;++c=this.__values__.length;return{done:n,value:n?T:this.__values__[this.__index__++]}},An.prototype.plant=function(n){ for(var t,r=this;r instanceof En;){var e=Fe(r);e.__index__=0,e.__values__=T,t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},An.prototype.reverse=function(){var n=this.__wrapped__;return n instanceof Un?(this.__actions__.length&&(n=new Un(this)),n=n.reverse(),n.__actions__.push({func:Qe,args:[Ge],thisArg:T}),new On(n,this.__chain__)):this.thru(Ge)},An.prototype.toJSON=An.prototype.valueOf=An.prototype.value=function(){return wr(this.__wrapped__,this.__actions__)},An.prototype.first=An.prototype.head, wi&&(An.prototype[wi]=Xe),An}();typeof define=="function"&&typeof define.amd=="object"&&define.amd?($n._=rt, define(function(){return rt})):Nn?((Nn.exports=rt)._=rt,Fn._=rt):$n._=rt}).call(this);; const isLayout2024 = document.documentElement.classList.contains("layout-2024"); function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } const isFilterEmpty = function(){ return document.querySelectorAll('.filters a.filter.selected').length === 0; } /** * update category cell divider class and hide the cell if condition meet * getScreenWidth function use to support the old browser to get screen width * * @param {String} type: To identify calling from which function * @param {Booleon} isShowAll: To control hide or show the cell * @access Public. */ const updateCategoryCells = function (type,isShowAll) { // console.log(`update cell trigger:${type} showall:${isShowAll}`) // Get all category-cell elements const categoryCells = document.querySelectorAll('#category-grid .category-cell'); let allGroup = []; let currentGroup = []; let maxCellsPerGroup = 6; let maxCellsPerRow = 2; function getScreenWidth() { return window.innerWidth || document.documentElement.clientWidth; } if(getScreenWidth() >= 768){ maxCellsPerGroup = 9; maxCellsPerRow = 3; } // adjust image cell size to 1 if only have 1 item on the last row function adjustImgCellSize(categoryGroup){ categoryGroup.forEach(subcatGroup => { const CellGroup = subcatGroup.currentGroup; // if cell img exist on this subcat group and span 2 slot if(CellGroup[0].classList.contains("category-cell-img")){ // If maxCellsPerRow is 2, set data-size to 2 if (maxCellsPerRow == 2) { CellGroup[0].setAttribute("data-size", "2"); } else { // Calculate total items (including img cell spanning two columns) const totalItems = [...CellGroup].length + 1; // img cell spans two columns, so it takes two slots // Calculate remaining items const remainingItems = totalItems % maxCellsPerRow; // console.log(`total${totalItems} % maxcellperrow${maxCellsPerRow} = remain${remainingItems}`) // If subcat last row showing only 1 item, img cel use size 2 instead of 1 CellGroup[0].setAttribute("data-size", remainingItems == 1 ? "1" : "2"); } } }); } function isLastCell(index){ return index == categoryCells.length-1; } categoryCells.forEach((cell, index) => { const subcat = cell.getAttribute('data-subcat'); const firstIndex = subcat.split(',')[0]; // check if is divider cell or is pdt cell if (cell.classList.contains('series-divider')) { //if is series divider cell //also mean this is the last cell of the subcat group allGroup.push({firstIndex,currentGroup}) // collect group if(!isShowAll){ //reset divider hidden status cell.classList.remove('hidden'); } // Reset group tracking for the next group currentGroup = []; } else if (subcat.startsWith(firstIndex)) { // if is pdt cell // if this pdt cell hidden and filter is empty, remove hidden if(isFilterEmpty() && cell.classList.contains('hidden')){ cell.classList.remove('hidden'); } currentGroup.push(cell); //collect cell //coz last subcat dun have divider, collect the last subcat here if(isLastCell(index)){ allGroup.push({firstIndex,currentGroup}) // collect group } } }); adjustImgCellSize(allGroup); } // sortCategoryCells obj // use flex order css to sort the cell '#category-grid .category-cell' // argument: sort, order // if no any filter selected, order need to restart after .viewall // argument sort option:popular|newest|price // argument order option:asc|desc // popular will be default, which mean just need to remove the order, no need sort chking option // newest need to check the attribute 'data-newflag' number from .category-cell, from biggest number to lowest number, no need sort chking option // price need to check the attribute 'data-price' number from .category-cell, only price need to check sort direction const sortCategoryCells = function (sort, order) { // collect cell from category grid const categoryCells = Array.from(document.querySelectorAll('#category-grid .category-cell')); let sortedCells = []; let cellPrice = ""; //toggle all divider from '#category-grid .category-cell.series-divider' using class hidden const toggleDivider = function(action){ const dividerCells = Array.from(document.querySelectorAll('#category-grid .category-cell.series-divider')); if(action == 'hidden'){ dividerCells.forEach(cell => cell.classList.add('hidden')); }else{ dividerCells.forEach(cell => cell.classList.remove('hidden')); } }; //remove the extra space if(document.querySelector("#category-grid > .clearfix")){ document.querySelector("#category-grid > .clearfix").classList.add('hidden'); } if (sort === 'popular') { //remove order css from all the elem inside category cell categoryCells.forEach(cell => cell.style.order = ''); //only when popular called and filter empty, reset the cell to default pattern if(isFilterEmpty()) { updateCategoryCells("sortby_click",false); toggleDivider('show'); } } else if (sort === 'newest') { // Sort categoryCells based on data-newflag and update the order sortedCells = [...categoryCells].sort((a, b) => { return order === 'asc' ? a.getAttribute('data-newflag') - b.getAttribute('data-newflag') : b.getAttribute('data-newflag') - a.getAttribute('data-newflag'); }); sortedCells.forEach((cell, index) => { cell.style.order = index + 1; }); toggleDivider('hidden'); } else if (sort === 'price') { // Sort categoryCells based on data-price and update the order sortedCells = [...categoryCells].sort((a, b) => { return order === 'asc' ? a.getAttribute('data-price') - b.getAttribute('data-price') : b.getAttribute('data-price') - a.getAttribute('data-price'); }); sortedCells.forEach((cell, index) => { cellPrice = cell.getAttribute('data-price'); if (cellPrice == null || cellPrice === '') { //move the no price cell to last position cell.style.order = sortedCells.length + 1; }else{ cell.style.order = index + 1; } }); toggleDivider('hidden'); } } if(isLayout2024){ /** * Sets up a window resize listener that triggers an update function * only when the window width crosses specified breakpoints. * * and values are objects with 'min' and 'max' properties defining the width range. * @param {Function} update - The function to call when the window width crosses from one * breakpoint range to another. * * setupResponsiveUpdate(update); */ function setupResponsiveUpdate(update) { const breakpoints = { 1: { min: 0, max: 767 }, 2: { min: 768, max: Infinity } }; let currentBreakpoint = getBreakpoint(window.innerWidth || document.documentElement.clientWidth); function getBreakpoint(width) { return Object.keys(breakpoints).find(key => width >= breakpoints[key].min && width <= breakpoints[key].max ); } function handleResize() { // console.log("resize triggered"); const newBreakpoint = getBreakpoint(window.innerWidth || document.documentElement.clientWidth); // console.log(`newBreakpoint ${newBreakpoint} currentBreakpoint ${currentBreakpoint}`) if (newBreakpoint !== currentBreakpoint) { // console.log(`Breakpoint changed from ${currentBreakpoint} to ${newBreakpoint}`); currentBreakpoint = newBreakpoint; update(); // console.log("breakpoint change, update") } } // Listens for changes in the window size, typically triggered by resizing the browser window. window.addEventListener('resize', handleResize); // Listens for changes in device orientation, such as when switching between portrait and landscape modes. window.addEventListener('orientationchange', handleResize); handleResize(); // init } setupResponsiveUpdate(function(){ if(isFilterEmpty()){ // to check the extra review cell and hidden if need debounce(updateCategoryCells("window_resize",false), 200); } }); } /** * The grid. * /!\ This is a modified version of the grid library bellow. * For this particular use, cell sizes are given in classes (e.g. width-x2) rather than data attribute. * The second change is that this version handles different image src per cell. to provide in data attributes (e.g. data-src-x2). * * @author Antoni Andre. http://antoniandre.me * @see http://github.com/antoniandre/grid * @version 1.0 * @since 2016-10-21 * @license MIT */ var thegrid = function (options) { /** * Private vars. * We don't want to mess them up from outside the class. * * @access private. */ var self = this, grid, cells, cellsNum, cellWidth, cellHeight, updateImgSrc, sorting, hasBreakpoints, breakpointsArray, defaults = { grid: $('.thegrid'),// The container of the cells. // This var will be removed after init() to avoid circular error due to recursion. // It is stored in public var 'grid' instead. cells: $('.cell'),// The cells to render / place / filter. cellsPerRow: 7, cellHeight: 100,// Cell height in pixels. // You can also define a function like so: // function(gh){return ((gh.gridWidth / gh.options.cellsPerRow) * 262 / 400) + 100}; // But be aware that it will be much more costy (you may opt for throttling). //========= Animations =========// // By default the cells animation will be handled via CSS transitions as it is known to be smoother and // of better performances. however if you want some fancy easing effects (e.g. elastic or bounce) you may // want to switch to javascript animations. If so just turn animationPlatform to 'js' and provide the easing // speed and curve that you want. // You can also override the default CSS transitions in a custom CSS like: // .thegrid.transitions .cell { // -webkit-transition: .3s ease-in-out; // -o-transition: .3s ease-in-out; // transition: .3s ease-in-out; // } animationPlatform: 'css',// 'css' or 'js'. animationEasing: 'swing',// jQuery built-in easings are 'swing', 'linear'. the jquery.easing plug-in can add many more. animationSpeed: 500, animationDelay: 0, updateGridHeight: true,// On each render. //========= Responsiveness =========// // Only if breakpoints are set the redraw() function is triggered on each resize event. // You can throttle the redraw to alight the resizing event. // (while resizing the redraw() function is exec every throttlingDelay milliseconds) throttling: false, throttlingDelay: 300,// in ms. //========= Breakpoints =========// // (from desktop to mobile). // Use it to allow a different behavior according to the device screen width. // Example of use: // breakpoints: // { // 1199: // { // cellsPerRow: 5 // }, // 767: // { // cellsPerRow: 4 // }, // 479: // { // cellsPerRow: 3 // }, // } breakpoints: {}, //========= Sorting =========// // If you want to apply sorting on The Grid cells. // Each criterion that you use for sorting can apply a numeric or string sorting. // This allows to sort naturally: numeric to [1, 3, 11], or string to ['1a', '11a', '3a']. // Note that for performance concerns this is set as a parameter and not as a native guess. // Example of use: // sortingCriteria: // { // name: {type: 'string'}, // price: {type: 'int'}, // } sortingCriteria: {} }, /** * Internal matrix class to handle all the calculations. * Represent the grid as a matrix like: * [0 0 0 0 * 0 0 0 0 * 0 0 0 0 * 0 0 0 0]. * Don't forget the optimization is very important as we trigger the redraw() function on resize event. * The inject() method will inject the given cell at a given position providing cell dimension in units. * * @access Private. */ gridMatrix = { matrix: [],// The matrix in use for placing cells. cols: 0,// Cache the cols number for optimization as used few times in this class. rows: 0,// Cache the rows number for optimization as used few times in this class. emptyCells: [],// Index the empty cells for faster results. /** * Creates the matrix with the specified dimensions. * And fill each cell with Null to represent cell availability. * * @access Public. * @return {Object} The current instance. */ init: function () { this.emptyCells = []; this.cols = self.options.cellsPerRow; this.rows = Math.ceil(cellsNum / this.cols); this.matrix = matrix(this.cols, this.rows, null); // First index the whole matrix cells in this.emptyCells array as every cell is empty. // Indexing the empty cells for optimization. for (var row = 0; row < this.rows; row++) { // Creating decimal format to benefit from the simple numeric sorting. for (var col = 0; col < self.options.cellsPerRow; col++) this.emptyCells.push(row + "." + col); } return this; }, /** * Adds an empty cell to the quick index array - for optimization. * * @access Public. * @param {String} position: The [x, y] coordinates in matrix. */ addEmptyCell: function (position) { this.emptyCells.push(position[1] + '.' + position[0]); this.emptyCells.sort(function (a, b) { return a - b }); }, /** * Removes an empty cell. * * @access Public. * @param {String} position: The [x, y] coordinates in matrix. */ removeEmptyCell: function (position) { // IE8-: // for (var cell = 0, l = this.emptyCells.length; cell < l; cell++) { // if (this.emptyCells[cell] === position[1] + ',' + position[0]) this.emptyCells.splice(cell, 1); // } // Modern browsers: var index = this.emptyCells.indexOf(position[1] + '.' + position[0]); if (index > -1) this.emptyCells.splice(index, 1); this.emptyCells.sort(function (a, b) { return a - b }); }, /** * Inject the cell content in matrix. * The matrix here won't contain any HTML it will only hold the given DOM cell index. * * @access Public. * @param {String} cellContent: The cell content * @param {String} dimension: The dimension * @param {String} position: The [x, y] coordinates in matrix. * @return {Array} The [x, y] coordinates in matrix. */ inject: function (cellContent, dimension, position) { // Store the dimension (in units) of the cell we want to inject in matrix. var dim = { w: dimension[0], h: dimension[1] }, fillCellsWidth = dim.w, fillCellsHeight = dim.h; // At what matrix position do we inject the cell content. if (position === undefined) position = this.getNextEmptyCell(); // Inject when a matrix position where cell content is fitting is found. while (!this.canFit(dimension, position)) { position = this.getNextCell(position); } // Now that a place is found in matrix to fit the content fill the needed matrix cells. var x = position[0], y = position[1]; while (fillCellsWidth > 0) { this.matrix[y][x] = cellContent; this.removeEmptyCell([x, y]); fillCellsHeight = dim.h - 1; while (fillCellsHeight > 0) { y++; this.matrix[y][x] = cellContent; this.removeEmptyCell([x, y]); fillCellsHeight--; } fillCellsWidth--; x++; } return position; }, /** * Gets the next empty cell. * * @access Public. * @return {Array} The next empty cell [x, y] coordinates in matrix. */ getNextEmptyCell: function () { if (!this.emptyCells.length) return this.addNewRow(); var yx = this.emptyCells[0].split('.'); // console.log('getNextEmptyCell', this.emptyCells, 'returning first empty cell: [x:'+yx[1]+', y:'+yx[0]+']', [parseInt(yx[1]), parseInt(yx[0])]); return [parseInt(yx[1]), parseInt(yx[0])]; }, /** * Gets the next cell whether it is empty or not. * * @access Public. * @param {Array} position: The [x, y] coordinates in matrix. * @return {Array} The next cell [x, y] coordinates in matrix. */ getNextCell: function (position) { if (position === undefined) position = [0, 0]; // console.log('getNextCell returns next existing cell position:', this.matrix[position[1]][position[0] + 1] !== undefined ? [position[0] + 1, position[1]] : [0, position[1] + 1]) // : [0, position[1] + 1]) // Look for a cell on the right of the current one, if none take the first one of the following row. return this.matrix[position[1]][position[0] + 1] !== undefined ? [position[0] + 1, position[1]] : [0, position[1] + 1]; }, /** * Adds a new row to the matrix. * * @access Public. * @return {Array} The [x, y] coordinates in matrix of the first cell of the new row. */ addNewRow: function () { this.matrix.push(matrix(this.cols, 1, null)[0]); this.rows++; for (var i = 0; i < this.cols; i++) this.addEmptyCell([i, this.rows - 1]); return [0, this.rows - 1]; }, /** * Determines if cell is empty at the given position. * * @access Public. * @param {Array} position: The [x, y] coordinates in matrix. * @return {Boolean} True if cell is empty, False otherwise. */ isCellEmpty: function (position) { if (this.matrix[position[1]] === undefined) { this.addNewRow(); return true; } return this.matrix[position[1]][position[0]] === null; }, /** * Determines the ability to fit the given cell in matrix at a given position. * * @access Public. * @param {Array} dimension: The dimension (in units) of the cell you want to put in matrix [width, height]. * @param {Array} position: The [x, y] coordinates where to place the cell in matrix. * @return {Boolean} True if able to fit, False otherwise. */ canFit: function (dimension, position) { var w = dimension[0], h = dimension[1], x = position[0], y = position[1], canFitWidth = 0, canFitHeight = 0; // If the cell exists (different of undefined) and is empty. for (var i = x; i < x + w; i++) if (this.isCellEmpty([i, y]) === true) { canFitWidth++; } for (var j = y; j < y + h; j++) if (this.isCellEmpty([x, j]) === true) { canFitHeight++; } // console.log('cell with dimension [w: '+w+', h: '+h+'] can fit at position [x:'+x+', y:'+y+']', (canFitWidth >= w && canFitHeight >= h)) return canFitWidth >= w && canFitHeight >= h; } }; // Public vars. self.throttleTimer = null; // Uncomment for debugging. // self.gridMatrix = gridMatrix; /** * Loop through each DOM element in the 'cells' collection and place them in the created matrix according to availability. * Called internally by init() and redraw(). * * @access Private. */ var fillMatrix = function () { // Create the matrix to the specified dimensions. gridMatrix.init(self.options.cellsPerRow, Math.ceil(cellsNum / self.options.cellsPerRow), null); cells.each(function (i, cell) { cell = $(cell); // var unitWidth = Math.min(cell.data('width'), self.options.cellsPerRow) || 1, // unitHeight = cell.data('height') || 1, var unitWidth = Math.min(cell.hasClass('width-x4') ? 4 : (cell.hasClass('width-x2') ? 2 : 1), self.options.cellsPerRow), unitHeight = cell.hasClass('height-x2') ? 2 : 1, matrixPosition = gridMatrix.inject(i, [unitWidth, unitHeight]); // Remember the matrix position in DOM pure js element itself. cell[0].matrixPosition = { x: matrixPosition[0], y: matrixPosition[1] }; }); return this; }; /** * Render the grid. * Called internally by init() and redraw(). * * @access Private. */ var render = function () { // On each render, call the cellHeight function if it is defined and update the cell height. if (typeof self.options.cellHeight === 'function') { self.gridWidth = gridWidth = grid.width(); cellHeight = self.options.cellHeight(self); } cells.each(function (i, cell) { cell = $(cell); // If reduce screen until a cell width doesn't fit grid width then constrain it to the grid width. // var unitWidth = cell.data('width') || 1, // unitHeight = cell.data('height') || 1, var unitWidth = Math.min(cell.hasClass('width-x4') ? 4 : (cell.hasClass('width-x2') ? 2 : 1), self.options.cellsPerRow), unitHeight = cell.hasClass('height-x2') ? 2 : 1, newCss = (isLayout2024) ? {} : { position: 'absolute', width: (cellWidth * Math.min(unitWidth, self.options.cellsPerRow)) + '%', height: cellHeight * unitHeight, top: cell[0].matrixPosition.y * cellHeight, left: (cell[0].matrixPosition.x * 100 / self.options.cellsPerRow) + '%', }; if ($('body').innerWidth() < 375) { newCss.position = 'initial' newCss.top = 'auto' newCss.left = 'auto' if (cell.hasClass('category-banner')) { newCss.height = 'auto' } } setTimeout(function () { self.options.animationPlatform === 'js' ? cell.stop(true, true).animate(newCss, self.options.animationSpeed, self.options.animationEasing) : cell.css(newCss); }, self.options.animationDelay); var img = cell.find('img'); img.attr('src', img.attr('data-src-x' + Math.max(unitWidth, unitHeight))); }); // After the cells positionning the grid height might have changed. Update if updateGridHeight is set to true. if ($('body').innerWidth() < 375) { $('#category-grid').height('auto') } else { const newHeight = gridMatrix.rows * cellHeight; if (self.options.updateGridHeight) { grid.stop(true, true) .animate({ height: newHeight > 0 ? newHeight : 'auto' }, { duration: self.options.animationSpeed, easing: 'easeOutCirc' }); } } return this; }; /** * Update parameters. * * @access Public. * @param {Object} params: The parameters to update. Will be overriding the current parameters. * @return {Object} The current instance. */ self.updateParams = function (params) { // Merge current settings and overriding new ones given as parameter. self.options = $.extend(self.options, params); cellWidth = 100 / self.options.cellsPerRow; cellHeight = self.options.cellHeight; return this; }; /** * Show or hide the given collection of cells retaining the given order. * * @access Public. * @param {jQuery Collection or selector string} cellsToToggle: either a css selector to match a collection of cells * or a jQuery collection of cells to toggle. * @param {Boolean} hide: whether to show or hide the given collection of cells. Default: false. * @param {Boolean} toggleAllOthers: whether to show or hide all the other cells that are not in the given collection. Default: false. * @return {Object} The current instance. */ self.filter = function (cellsToToggle, hide, toggleAllOthers) { hide = hide !== undefined ? hide : false; toggleAllOthers = toggleAllOthers !== undefined ? toggleAllOthers : false; // In case you the provide collection is not a jQuery object. cellsToToggle = cellsToToggle instanceof jQuery ? cellsToToggle : $(cellsToToggle); cellsToShow = hide ? (toggleAllOthers ? $(self.options.cells).not(cellsToToggle) : null) : cellsToToggle; cellsToHide = hide ? cellsToToggle : (toggleAllOthers ? $(self.options.cells).not(cellsToToggle) : null); // If the given selection is to show, keep its specific order in case of prior sorting. if (isLayout2024) { if (cellsToShow) cellsToShow.removeClass('hidden'); if (cellsToHide) cellsToHide.addClass('hidden'); }else{ if (cellsToShow) cellsToShow.css({ top: 0, left: 0 }).removeClass('hidden'); if (cellsToHide) { cellsToHide.addClass('hidden'); setTimeout(function () { cellsToHide.css({ top: 0, left: 0 }) }, self.options.animationSpeed); } } cells = cellsToShow; cellsNum = cells.length; // if (sorting) self.sort();// New feature. return this; }; /** * Sort the grid cells according to the active sorting from options.sortingCriteria. * * @access Public. * @return {Object} The current instance. */ /* New feature: self.sort = function() { if (!sorting) return; var sortedCells = null; for (var criterion in sorting) if (sorting[criterion].active) { sortedCells = cells.slice(0).sort(function(a, b) { var multiplier = sorting[criterion].order === 'asc' ? 1 : -1, ret; a = a.attributes["data-" + criterion].value || ''; b = b.attributes["data-" + criterion].value || ''; switch(sorting[criterion].type) { case 'int': case 'float': a = parseFloat(a); b = parseFloat(b); ret = a - b; break; default: ret = a < b ? -1 : (a > b) ? 1 : 0; break; } return ret * multiplier; }); cells = sortedCells || sorting.default; } return this; };*/ self.sort = function (sortedCells) { cells = $(sortedCells).not('.hidden'); cellsNum = cells.length; return this; }; /** * Initialize the sorting feature if sortingCriteria are given in options. * * @access Private. * @return {Object} The current instance. */ var initSort = function () { sorting = {}; for (var criterion in self.options.sortingCriteria) { sorting[criterion] = self.options.sortingCriteria[criterion]; sorting[criterion].sorted = cells; } // Clone the array of cells to make sure their order won't be affected by any later change of cells var. sorting.default = cells.slice(0); $('[data-sort]').on('change', function (e) { this.pos = ((this.pos || 0) + 1) % 3; var order = ['', 'asc', 'desc'][this.pos]; criterion = $(this).data('sort'); $(this).attr('data-order', order); sorting[criterion].order = order; sorting[criterion].active = this.pos; if(!isLayout2024) self.sort().redraw(); }); return this; } /** * Reset all the applied filters and go back to the original cells collection from the DOM. * * @access Public. * @return {Object} The current instance. */ self.resetFilters = function () { cells = $(self.options.cells).removeClass('hidden'); cellsNum = cells.length; // if (sorting) self.sort();// New feature. return this; } /** * Redraw function to: * 1 - recalculate the disposition of cells in grid according to new dimensions and options. * 2 - render the new grid disposition. * Then trigger the redraw event. * * @access Public. * @return {Object} The current instance. */ self.redraw = function () { fillMatrix(); render(); grid.trigger('redraw'); return this; }; /** * Bind the grid related events on grid init. * * @access Private. * @return void. */ var bindEvents = function () { if (hasBreakpoints || typeof self.options.cellHeight === 'function') { $(window).on('resize gridInit afterResize', function (e) { // On resize, if throttling is on, exit quickly for better performance. // So keep this first. if (self.options.throttling && e.type === 'resize') { // Only trigger a custom 'afterResize' callback event and return. // So that, we recalculate at most once every options.throttlingDelay seconds. self.throttleTimer = setTimeout(function () { clearTimeout(self.throttleTimer); self.throttleTimer = null; $(window).trigger('afterResize'); }, self.options.throttlingDelay); return true; } // Watch breakpoints and apply new config if a set of breakpoints is defined. var breakpointChange = hasBreakpoints ? watchBreakpoints(e) : false; // Only redraw if current breakpoint has changed or if we want to recalculate height at each step. if (breakpointChange || typeof self.options.cellHeight === 'function') { self.redraw(); } }); } }; /** * On window resize, gridInit and afterResize events, watch if the screen width reaches a breakpoint * and update the configuration accordingly by applying the new breakpoint's config over the current one. * * @access Private. * @return {boolean} true if the breakpoint has changed, false otherwise. */ var watchBreakpoints = function (e) { var params = {}, screenWidth = this.innerWidth, tmpBp = [], currentBreakpoint, newBreakpoint, breakpointChange = false; updateImgSrc = false; // Best optimized code possible: // Way faster than for loop on given breakpoints. // Also faster than a series of if / else if. // The idea is to have an array of breakpoints and add the current screenWidth in it and ASC sort the array. // Then locate the position of the screenWidth in array and get the breakpoint just above to get its config. tmpBp = breakpointsArray.slice(0);// Clone array. tmpBp.push(screenWidth);// Add screenWidth to the array. // Sort NATURAL ASC and always put '++' (for init config) at the end (e.g. [479, 767, 790, 1199, "1199++"]). tmpBp.sort(function (a, b) { return (typeof a === 'string' && a.indexOf('++') > -1) || parseInt(a) - parseInt(b) }); var i = tmpBp.indexOf(screenWidth);// Get the position of screenWidth in the breakpoints array. // If the index is the last one (initial and widest screen config), breakpointsArray[i] won't exist so // just reapply initial config. newBreakpoint = breakpointsArray[i]; breakpointChange = newBreakpoint !== currentBreakpoint; if (breakpointChange) { currentBreakpoint = newBreakpoint; // Trigger the breakpointChange event but skip the init one. if (e.type !== 'gridInit') grid.trigger('breakpointChange', [currentBreakpoint]); updateImgSrc = true; // Apply the settings of the detected breakpoint config before redrawing grid. params = self.options.breakpoints[currentBreakpoint]; self.updateParams(params); } return breakpointChange; } /** * If breakpoints are defined, parse the different specific configs and optimize/cache all configs and * breakpoints values into a breakpointsArray for the best performances possible in resize event loop. * * @access Private. * @return void. */ var initBreakpoints = function () { var breakpointsNum, initialConfigIndex = 0; hasBreakpoints = true;// Update private class var for convenient/meaningful use in different methods. breakpointsArray = []; // Convert to breakpoints object an array for sorting. for (var breakpoint in self.options.breakpoints) breakpointsArray.push(breakpoint * 1); breakpointsNum = breakpointsArray.length; // NATURAL numeric ascendant sorting (*1 is shorthand for parseInt()). breakpointsArray.sort(function (a, b) { return a * 1 - b * 1 }); // Add the initial config (i.e. 'widest') into the breakpoints object and breakpoints array. // E.g. [479, 767, 1199, "1199++"]. // It would be easier to have all integers but outputting breakpoint number to the end user through the breakpointChange // event has to be meaningful. initialConfigIndex = breakpointsArray[breakpointsNum - 1] + '++'; self.options.breakpoints[initialConfigIndex] = JSON.parse(JSON.stringify(self.options)); delete self.options.breakpoints[initialConfigIndex].breakpoints;// Don't need to keep this. breakpointsArray.push(initialConfigIndex); }; /** * Init the grid, only once. This is the entry point and self-called. * Set the grid options from parameters, * bind the grid events, * fill the matrix with cells in DOM and render for the first time. * * @access Private. * @return void. */ var init = function () { // Merge default settings and overriding options given as parameter. self.options = $.extend(defaults, options); // Init the core vars. grid = $(self.options.grid); delete self.options.grid;//!\\ Avoid circular error due to recursion. self.gridWidth = grid.width(); cells = $(self.options.cells).not('.hidden'); cellsNum = cells.length; cellWidth = 100 / self.options.cellsPerRow; cellHeight = self.options.cellHeight; // First set the grid approximate height before calculating the cells position. // After the cells placing the grid height will probably be different. grid.css('height', Math.ceil(cellsNum / self.options.cellsPerRow) * self.options.cellHeight); if (!$.isEmptyObject(self.options.sortingCriteria)) initSort(); if (!$.isEmptyObject(self.options.breakpoints)) initBreakpoints(); bindEvents(); if(!isLayout2024){ fillMatrix(); render(); } // Force check current breakpoint at init. if (!$.isEmptyObject(self.options.breakpoints)) $(window).trigger('gridInit'); // Trigger ready custom event available for external use. grid.trigger('ready').addClass('ready' + (self.options.animationPlatform === 'js' ? '' : ' transitions')); }(); return self;// Allow chaining. ;) }, /** * Helper function. * Creates a matrix of the given size and fill each cell with a default value. * * @param {number} cols The columns number. * @param {number} rows The rows number. * @param {mixed} defaultValue The default value for each cell. * @return {Array} the generated matrix. */ matrix = function (cols, rows, defaultValue) { for (var i = 0, arr = []; i < rows; i++) { arr.push([]);// Creates an empty line. arr[i].push(new Array(cols));// Adds cols to the empty line. for (var j = 0; j < cols; j++) arr[i][j] = defaultValue;// Initializes. } return arr; }; /** * Eventually make the jQuery plug-in wrapper. * * @param {Object|String} firstArg: can hold either a method name or an object of parameters for init. * @return {Object} The current instance. */ $.fn.grid = function (firstArg) { // The DOM element on which we call the grid plugin. var gridElement = this[0]; var gridElement = this[0], warn, error; // Handle errors and incorrect grid calls. switch (true) { case (!gridElement || gridElement === undefined): error = 'Can\'t instantiate The Grid on an empty jQuery collection.'; break; case (['object', 'undefined', 'string'].indexOf(typeof firstArg) === -1): warn = 'Ignoring grid call with wrong params.'; break; case (typeof firstArg === 'string' && typeof (gridElement).grid[firstArg] !== 'function'): warn = 'Ignoring unknown Grid method call "' + firstArg + '".'; break; } if (warn || error) console[error ? 'error' : 'warn'](warn || error); // Instantiate The Grid. else if (typeof firstArg === 'object' || firstArg === undefined) { (firstArg || { grid: null }).grid = gridElement; gridElement.grid = new thegrid(firstArg); } // Call a grid method (with params) from its name as a string. // E.g. $('.thegrid').grid('filter', [cellsToToggle, hide, toggleAllOthers]); // First check method exists before calling it. else if (typeof firstArg === 'string' && gridElement !== undefined && typeof (gridElement).grid[firstArg] === 'function') { // Extract rest arguments from built-in 'arguments' pseudo-array (no array method avail on arguments). var args = [].slice.call(arguments, 1); // Call the object method with given args. gridElement.grid[firstArg].apply(this, args); } return this; }; ; var CompareChart = { cookieName: 'comcart', categoryID: 0, languageID: 1, itemCount: 4, compareServer: 'http://asia.test.creative.com/compare', init: function (options) { if (typeof (options.compareServer) != 'undefined') { this.compareServer = options.compareServer; } if (typeof (options.categoryID) != 'undefined') { this.categoryID = options.categoryID; } if (typeof (options.languageID) != 'undefined') { this.languageID = options.languageID; } // Config underscore settings _.templateSettings = { evaluate: /\{%([\s\S]+?)%\}/g, // excute code: {% code_to_execute %} interpolate: /\{\{(.+?)\}\}/g, // print value: {{ value_name }} escape: /\{%-([\s\S]+?)%\}/g }; this.bindEvents(); }, show: function () { var products = this.getProduct(); if (products.length > 0) { this.loadHtml(products.length); } else { this.clear(); } }, bindEvents: function () { var self = this; $("#compare-button-2").on("click", function (e) { e.preventDefault(); var products = self.getProduct(); if (products.length > 1) self.compare(); else notificationBar.show(' ' + res.txtCompareProductsErrorInfo, 4000); }); $("#compare-results .close").click(function (e) { e.preventDefault(); $("html").removeClass("no-scroll"); $("#compare-results").toggleOverlay(); }); $("#compare-actions .close").on("click", function (e) { e.preventDefault(); $("#compare-products").removeClass("open"); $(".category-cell.selected").removeClass("selected"); $("#compare-button-1").fadeTo(0, 1);// Not .show()/.hide() to keep height and prevent small jump of products. $(".category-cell").removeClass("compare cannot-compare").off("click"); }); $(document).on("click", ".compare-selected-product .compare-remove-product", function (e) { e.preventDefault(); var products = self.getProduct(); var productID = $(this).parent() .parent() .data("product-id"); self.removeProduct(productID); }) $("#compare-button-1").on("click", function (e) { e.preventDefault(); var el = $(this); if (el.is(":visible")) { el.fadeTo(0, 0); self.loadCompareProducts(); $("#compare-products").addClass("open"); $(".category-cell").each(function (i, e) { var el = $(e); var productID = el.data("productid"); var compareProducts = self.getProduct(); if (el.hasClass("can-compare")) { if ($.inArray(parseInt(productID), compareProducts) == -1) { el.find(".compare-overlay > div").html(res.txtAddToCompare); } else { el.find(".compare-overlay > div").html(res.remove_from_compare); } } else { el.addClass("cannot-compare"); el.find(".compare-overlay > div").html(res.na_to_compare); } }) .addClass("compare") .on("click", function (e) { e.preventDefault(); var productID = $(this).data("productid"); self.addProduct(productID); }) } else { el.fadeTo(0, 0); $("#compare-products").removeClass("open"); } }); }, loadCompareProducts: function () { if ($(".compare-selected-product").length === 0) { var compareProducts = this.getProduct(); for (var i = 0; i < compareProducts.length; i++) { $(".category-cell[data-productid=" + compareProducts[i] + "]") .addClass("selected") .each(function (i, e) { var el = $(e); var productID = el.data("productid"); var link = el.find(".fulllink").attr("href"); var image = "https://img.creative.com/images/products/large/pdt_" + productID + ".png?width=120&height=120&mode=pad"; var name = el.find(".product-name").html().trim(); if (name.indexOf("new-tag") > -1) { name = name.substring(0, name.indexOf(" div") .html(res.remove_from_compare); } $("#compare-products").addClass("open"); $("#compare-button-2").removeClass("disabled"); } }, cutText: function (text, length) { if (text.length > length) { var desc = text; while (desc.length > length) { var newdesc = ""; var descGroup = desc.split(' '); for (var i = 0; i < descGroup.length - 1; i++) { newdesc += " " + descGroup[i]; } desc = newdesc; } desc = desc.toString() + "..."; return desc; } else { return text; } }, timeoutHolder: null, showNotice: function (message) { notificationBar.show(message, 10000); }, scroll: function () { $(window).scroll(function () { if ($(document).scrollTop() > $("#category-grid").offset().top + $("#category-grid").height() - $(window).height() + 150) { $("#cat-compare-holder").css("position", "absolute").css("bottom", "-128px").css("left", "2px"); } else { $("#cat-compare-holder").css("position", "fixed").css("bottom", "0").css("left", ($("#category-grid").offset().left + 2) + "px"); } }); }, clear: function () { $("#cat-compare-holder").remove(); $("#category-grid").css("margin-bottom", "0"); }, getProduct: function () { var result = new Array(); var cookies = $.cookie(this.cookieName + '-' + this.categoryID.toString()); if (cookies != null) { var ids = cookies.toString().split(','); for (var i = 0; i < ids.length; i++) { if (ids[i] != null && ids[i] != '' && !isNaN(ids[i])) { result.push(parseInt(ids[i])); } } } return result; }, addProduct: function (productID) { var products = this.getProduct(); var success = false; var el = $(".category-cell[data-productid=" + productID + "]"); var link = el.find(".fulllink").attr("href"); var image = "https://img.creative.com/images/products/large/pdt_" + productID + ".png?width=120&height=120&mode=pad"; var name = el.find(".product-name").html().trim(); if (name.indexOf("new-tag") > -1) { name = name.substring(0, name.indexOf("= this.itemCount) { if ($.inArray(parseInt(productID), products) === -1) { this.showNotice(res.txtCompareNotice); success = false; } } else { if (!isNaN(productID)) { if ($.inArray(parseInt(productID), products) === -1) { products.push(productID); this.updateCookie(products); success = true; } else { success = false; } } } } else { success = false; } if (success) { if (!el.hasClass("selected")) { el.addClass("selected"); el.find(".compare-overlay > div").html(res.remove_from_compare); var productHtml = tpl(product); $("#selected-products").append(productHtml); setTimeout(function () { $('#selected-products .compare-selected-product').removeClass('added'); }, 50); if (CompareChart.getProduct().length > 1) { $("#compare-button-2").removeClass("disabled"); } } } else { if (!el.hasClass("cannot-compare")) { this.removeProduct(productID); el.removeClass("selected"); el.find(".compare-overlay > div").html(res.txtAddToCompare); $(".compare-selected-product[data-product-id=" + productID + "]").remove(); if (CompareChart.getProduct().length < 2) { $("#compare-button-2").addClass("disabled"); } } } return success; }, removeProduct: function (productID) { var result = new Array(); var products = this.getProduct(); for (var i = 0; i < products.length; i++) { if (products[i] != null && products[i] != '' && !isNaN(products[i]) ) { if (parseInt(products[i]) != productID) { result.push(products[i]); } } } this.updateCookie(result); $(".category-cell[data-productid=" + productID + "]:not('.cannot-compare')").removeClass("selected") .find(".compare-overlay > div") .html(res.txtAddToCompare);; $(".compare-selected-product[data-product-id=" + productID + "]:not('.cannot-compare')").remove(); if (result.length < 2) { $("#compare-button-2").addClass("disabled"); } }, removeAll: function () { this.updateCookie(new Array()); }, updateCookie: function (products) { var value = ""; for (var i = 0, max = products.length; i < max; i++) { value += products[i]; if (i < products.length - 1) { value += ","; } } $.cookie(this.cookieName + '-' + this.categoryID.toString(), value); }, compare: function () { var products = this.getProduct(); var tpl = _.template($("#compare-results-template").html()); let isLive = !(location.hostname.toLowerCase().includes('dev') || location.hostname.toLowerCase().includes('stage') || location.hostname.toLowerCase().includes('localhost')) var url = isLive ? "https://oxrz6c4lbi.execute-api.ap-southeast-1.amazonaws.com/prod" : "https://if1k4cyjr4.execute-api.ap-southeast-1.amazonaws.com/dev"; url += "/compareresults?categoryid=" + this.categoryID + "&languageid=" + this.languageID + "&masterproductids=" + products.toString(); $("#compare-button-2").addClass("disabled"); $("#compare-results > .content").html('').parent().toggleOverlay().removeClass('ready'); $.get(url, function (data) { $("#compare-results > .content").html(tpl(data)).parent().addClass('ready'); $("#compare-results-products-header h2.product-name").each(function (i, j) { $(this).html($(".compare-selected-product:eq(" + i + ")").find(".product-name").html()); }) $("#compare-results-products-header .compare-product-link").each(function (i, j) { var productId = $(this).data("product-id"); this.href = $("div.category-cell[data-productid=" + productId + "] a.fulllink").attr("href"); console.log(this.href); }) $("html").addClass("no-scroll"); $("#compare-button-2").text(res.Page_compare) .removeClass("disabled"); }); }, };; var grid1, fh, compareProducts = [], PROMOPRODUCTLIST = [], categoryPage = function () { var self = this; this.minprice = 999, this.maxprice = 0, this.qGroup = [], this.oriItems = [], this.FilterOption = { subcat: [], pricerange: [], feature: [], freeShipping: 0, inStock: 0, pupular: false, pricerank: "asc", newest: true, products: [] }, this.PriceData = [], this.FeatureData = [], this.fetchPrices = parseInt($('#category-wrapper').data('get-prices') || 0), this.currentSort = null, this.GetPriceData = function (data) { $.each(data, function (i, n) { var removFlag = true; $.each(self.oriItems, function (k, j) { try { if (n.ProductID == $(j).data("productid")) removFlag = false; } catch (e) { } }); if (removFlag) n.Price = 0; }); self.PriceData = data; $.each(data, function (i, n) { if (n.Price > self.maxprice) self.maxprice = n.Price; if (n.Price < self.minprice && n.Price > 0.01) self.minprice = n.Price; }); var impressions = []; $.each(self.oriItems, function (i, n) { self.BindPrice(n); impressions.push(self.GTMBuildProductImpression(i, n)); }); self.GTMTrackProductImpression(impressions); return this; }, this.BindPrice = function (element) { var productid = $(element).attr("data-productid"); $.each(this.PriceData, function (i, n) { if (n.ProductID == productid) { var instock = "", freeShipping = "", newPrice = n.Price, oldPrice = n.ListPrice, priceHtml = n.ListPrice > n.Price ? n.PriceFormat.replace(".00", "") + " " + instock + " " + freeShipping + " " + n.ListPriceFormat.replace(".00", "") + "" : n.PriceFormat.replace(".00", "") + " " + instock + " " + freeShipping + ""; $(element) .attr("data-price", n.Price) .attr("data-free-shipping", n.FreeShipping === true ? "1" : "0") .attr("data-instock", n.InStock === true ? "1" : "0") .find("div.price").html(priceHtml); // console.log({ productid, instock:n.InStock, newPrice, oldPrice }); if (!n.InStock) { $(element).find('.oos-marker').show(); } if (n.Colors.length > 0) { const dedupedColors = Array.from(new Set(n.Colors)); if (dedupedColors.length > 1) { const swatches = dedupedColors.map(c => { return `
` }).join(''); $(element).find('.bottom-bar > .color-swatches') .attr('data-current-color', dedupedColors[0].toLowerCase().replace(/\s/g, '-')) .append(swatches) .addClass("ready"); } } if (typeof (n.IconOverlay) == "string" && n.IconOverlay.length) { var icons = n.IconOverlay.split(','); for (var i = 0; i < icons.length; i++) { if (icons[i].length) { var doImgTag = icons[i].toLowerCase().indexOf(".jpg") == -1 && icons[i].toLowerCase().indexOf(".gif") == -1 && icons[i].toLowerCase().indexOf(".png") == -1; $(element).find("div > div.icon-overlayer") .prepend(doImgTag ? ('') : ('' + icons[i] + '')); } } } if (n.ListPrice > n.Price && n.Price > 0.01 && global.store.id != 2) { var discount = Math.round((1 - newPrice / oldPrice) * 100); var domobj = $(element).find(".save-overlay"); var cssTop = "bottom: 0;"; if ($(element).hasClass('feature')) { var descObj = $("#productlist li.feature[data-productid='" + n.ProductID + "']").find("div > div.desc"); if (descObj.length) cssTop = "bottom: " + (descObj.height() + 20) + "px;"; } if (discount > 0) { domobj.html("

" + discount + "%
off

"); setTimeout(function () { domobj.addClass('show') }, 200); } } return; } }); }, //===========================================================================// // Google Tag Manager. tracking clicks. //===========================================================================// this.GTMTrackProductClicks = function () { $("ul#productlist li").on("click", "a.fulllink", function (e) { var productID = $(this).parent().parent().data("productid"); var productName = $(this).parent().parent().find(".name").text(); var productPrice = $(this).parent().parent().data("price"); var productCategory = $(".section-category .title:eq(0)").text().trim(); var pos = $(this).parent().parent().data("ranking"); dataLayer.push({ 'event': 'productClick', 'ecommerce': { 'click': { 'actionField': { 'list': 'Category Landing Page' }, 'products': [{ 'name': productName, 'id': productID, 'price': productPrice, 'brand': 'Creative', 'category': productCategory, 'variant': '', 'position': pos }] } } }); }); } this.GTMBuildProductImpression = function (index, obj) { var productID = $(obj).data("productid") || "0"; var productName = $(obj).find(".name").text(); var productPrice = $(obj).data("price"); return { 'name': productName, 'id': productID.toString(), 'price': productPrice, 'brand': 'Creative', 'category': $(".section-category .title:eq(0)").text().trim(), 'variant': '', 'list': 'Category Landing Page', 'position': (index + 1).toString() }; }, this.GTMTrackProductImpression = function (impressions) { dataLayer.push({ 'event': 'categoryPageDataLoaded', 'ecommerce': { 'currencyCode': currencyCode, 'impressions': impressions } }); }; //===========================================================================// this.hasShippingFilter = function (products) { return !!products.find(function (p) { return p.FreeShipping }) } // Allow chaining. return this; }; var fetchPrices = function () { $.ajax( { url: $('#category-wrapper').data('prices-api'), method: 'post', data: { h: global.store.customer.hashedEmail }, dataType: 'json', success: function (data) { catPage.GetPriceData(data); if (!catPage.hasShippingFilter(data)) { $('.fs-filter-shipping').remove(); } fh.initPriceRelatedFilters(data); if (urlParams.filters !== undefined) { filters = urlParams.filters.split(",").map(Number).filter(Boolean); $.each(filters, function (i, v) { $("#category-filter a[data-subcatid=" + v + "]").click(); }); } $('.color-swatches > div[class^=swatch-]').on('click', function (e) { e.preventDefault(); const currentColor = $(e.target).parent().data('current-color'); const selectedColor = $(e.target).data('color'); const container = $(e.target).parent().parent().parent().parent(); const slug = container.attr('href') .split('/') .pop(); const picture = container.find('picture'); const png = picture.find('img'); const webp = picture.find('source'); const pngPath = png.attr('src') .substr(0, png.attr('src').lastIndexOf('/') + 1); const pngQs = png.attr('src') .split('?') .pop(); const webpPath = webp.attr('srcset') .substr(0, webp.attr('srcset').lastIndexOf('/') + 1); const webpQs = webp.attr('srcset') .split('?') .pop(); const newPng = pngPath + 'cat-' + slug + '-' + selectedColor + '.png?' + pngQs; const newWebP = webpPath + 'cat-' + slug + '-' + selectedColor + '.webp?' + webpQs; png.attr('src', newPng); webp.attr('srcset', newWebP); $(e.target).parent().data('current-color', selectedColor); }) } }); }; /** * This filter is made to optimize the filtering system. * As filters are provided by the old API in an overly complicated way, the code here is overly complicated in self.init() * To put things back in its right place. */ var filtersHandler = function () { // Private vars. var self = this, cellsWrapper = $('#category-grid'), cells = cellsWrapper.find('.category-cell'), productIdList = [],// Will be filled up with the list of all the products in the page. indexedProductIdList = {},// Will be filled up with the list of all the products in the page indexed by their product id. productPrices = {},// Will be filled up with the list of products prices indexed by their product id. productsByFilters = {}, featuresParentsList = {}, postponedFiltersLoad = [],// May contain a list of filter ids from url that need to wait for the API results to be loaded. priceRange = { min: null, max: 0, from: 0, to: 0 };// The current page products price range in min/max and selected range in from/to (updated after call to price API). // Public vars. // self.productsList = {};// Will be filled up with the list of all the products in the page indexed by their product id. self.filteredCells = cells; self.activeFilters = {};// Will be filled up with a list of filter ids going in pair with a boolean for active state. E.g. {302: true, 356: true, 427: false}. self.activeSorting = { sort: 'popular', order: 'asc' }; /** * First call the filters API and execute the callback to fetch filters data. * * @access Private. * @return void. */ var fetchFiltersFromAPI = function (callback) { if ($('#category-wrapper').data('compare-api') !== undefined) { $.get($('#category-wrapper').data('compare-api'), callback) } }; /** * Init the category filters, The first layer of filters and most basic one. * * @access Private. * @return void. */ var initCategoryFilters = function () { cells.each(function (i, curr) { var productId = parseInt($(curr).data('productid')), catsPerProduct = $(curr).data('subcat') ? $(curr).data('subcat').split(',') : []; productIdList.push(productId); indexedProductIdList[productId] = true; // Hot filters. var hot = $(curr).data('hot') || null; if (hot) { if (productsByFilters['hotf'] === undefined) productsByFilters['hotf'] = []; productsByFilters['hotf'].push(productId); } // Index all products by category. for (var i = 0, l = catsPerProduct.length; i < l; i++) { if (productsByFilters[catsPerProduct[i]] === undefined) productsByFilters[catsPerProduct[i]] = []; productsByFilters[catsPerProduct[i]].push(productId); } }); }; /** * Init the features filters, The secind layer of filters and maybe most complex one. * First do a call to the Filters API and display the feature filters in a callback. * * @access Private. * @return void. */ var initFeatureFilters = function () { fetchFiltersFromAPI(function (data) { //enable compare button if (data && data.Products && data.Products.length > 0 && document.querySelector("#compare-button-1")) { document.querySelector("#compare-button-1").classList.add("ready"); } // Index features by parent filter id for faster/simpler use. // This system is over-complicated at the source... So here parse everything and generate the right indexes to facilitate filtering. for (var i = 0, l = data.Filters.length; i < l; i++) { // A useless wrapper only for display purpose. var filterGroup = data.Filters[i]; for (var j = 0, m = filterGroup.Filters.length; j < m; j++) { var filter = filterGroup.Filters[j], filterId = filter.ID;// The real filters. for (var k = 0, n = filter.Values.length; k < n; k++) { // The subfilters are called features. useless for filtering, so we'd better convert them to parent real filter. var feature = filter.Values[k]; featuresParentsList[feature] = filterId; } } } var comparableProducts = [] // Index products by id in productsList for faster use. for (var i = 0, l = data.Products.length; i < l; i++) { var product = data.Products[i], pid = parseInt(product.ProductID); // self.productsList[pid] = product.Values; if (indexedProductIdList.hasOwnProperty(pid)) comparableProducts.push(pid); // Index all products by Feature filter. for (var j = 0, m = product.Values.length; j < m; j++) { var feature = product.Values[j], index = featuresParentsList[feature] + 'f'; if (productsByFilters[index] === undefined) productsByFilters[index] = []; // Don't add if already in. if (productsByFilters[index].indexOf(pid) === -1 && indexedProductIdList.hasOwnProperty(pid)) productsByFilters[index].push(pid); } } $('[data-productid="' + comparableProducts.join('"],[data-productid="') + '"]').addClass('can-compare'); for (var i = 0, l = data.Filters.length; i < l; i++) { var html = renderFilter(data.Filters[i]); // if (html) $(".filters").append(html); if (html) $(".filters .other-filters").append(html); } if(isLayout2024){ // show first filter from other filters group $(".filters .other-filters .filter-group:first-of-type").removeClass("closed").children("ul").removeAttr("style"); } // If some filters from URL could not be loaded before this API result, then call the loadFiltersFromUrl() // again with only those filters in param. if (postponedFiltersLoad.length) loadFiltersFromUrl(postponedFiltersLoad); }); }; /** * Init the price related filters, The third layer of filters. * * @access Public. * @return void. */ self.initPriceRelatedFilters = function (productsList) { // Index all products by category. for (var i = 0, l = productsList.length; i < l; i++) { var pid = parseInt(productsList[i].ProductID), price = parseFloat(productsList[i].Price) || 0; priceRange.min = Math.min(priceRange.min === null ? 10000 : priceRange.min, price); priceRange.max = Math.max(price, priceRange.max); // Index all the prices by product id. productPrices[pid] = price; // Index all products by free shipping filter. (fsf alias Free Shipping Feature) if (productsList[i].FreeShipping) { if (productsByFilters['fsf'] === undefined) productsByFilters['fsf'] = []; productsByFilters['fsf'].push(pid); } // Index all products by in stock filter. (isf alias In Stock Feature) if (productsList[i].InStock) { if (productsByFilters['isf'] === undefined) productsByFilters['isf'] = []; productsByFilters['isf'].push(pid); } } $('.is-filter,.fs-filter').slideDown(300); if (priceRange.max) { priceRange.from = priceRange.min; priceRange.to = priceRange.max; $('.price-filter').slideDown(300) .find("#priceslider").attr('value', priceRange.from + ';' + priceRange.to) .slider( { from: parseInt(priceRange.from), to: parseInt(priceRange.to), step: 1, format: { format: res.CURRENCYFORMAT.replace(/NT\$/g, "$").replace(/\{0\:|\}/g, "").replace("#0.00", "##").replace("#0.00", "##").replace(/\#0\;/g, "##;").replace("0.00", "0") }, callback: function () { var slidervalue = $("#priceslider").slider("value").split(";"); priceRange.from = parseInt(slidervalue[0]); priceRange.to = parseInt(slidervalue[1]); self.updateFilters(); if(isLayout2024){ //if price filter slide on default position, reset cell if(priceRange.from == priceRange.min && priceRange.to == priceRange.max){ updateCategoryCells("pricefilter_move", false); } }else{ grid1.grid('redraw'); } } }); $('.jslider').on("touchstart touchmove touchend touchcancel", touchHandler); } else { $('#filter-instock').hide(); } // If some filters from URL could not be loaded before this API result, then call the loadFiltersFromUrl() // again with only those filters in param. if (postponedFiltersLoad.length) loadFiltersFromUrl(postponedFiltersLoad); // console.log('initPriceRelatedFilters'); }; /** * A filter consist of either a group of features or product categories - considered as subfilters. * Here the wrapping filter is not used to filter anything. hmmm.. Sth wrong in the source of this system. :/ * * @access Private. * @param {[type]} filter [description] * @return {html} the generated html for the features filter coming from the API. */ var renderFilter = function (filter) { var hasMatch = false, featuresHtml = '', features = filter.Filters || []; // If any feature (subfilter). for (var i = 0, l = features.length; i < l; i++) { if (productsByFilters[features[i].ID + 'f'] && productsByFilters[features[i].ID + 'f'].length) { featuresHtml += '
  • ' + features[i].Name + '
  • '; hasMatch = true; } } // Return the filter HTML if any subfilter matches anything or false otherwise. if(isLayout2024){ return hasMatch ? `
    ${filter.Name}
      ${featuresHtml}
    ` : null; }else{ return hasMatch ? `
    ${filter.Name}
      ${featuresHtml}
    ` : null; } }; /** * Apply a new filter. * * @access Public. * @param int filterId: the filter id to activate. * @return void. */ self.applyFilter = function (filterId) { self.updateFilters(filterId, true); }; /** * Release a filter. * * @access Public. * @param int filterId: the filter id to deactivate. * @return void. */ self.releaseFilter = function (filterId) { self.updateFilters(filterId, false); }; self.updateFilterCount = function (e) { $('.filter-group').each((i, j) => { const c = $(j).find('.selected').length; if (c > 0) { $(j).find('.title .filter-group-count').show().text(c); } else { $(j).find('.title .filter-group-count').hide(); } }) const priceFilterActive = (Math.floor(priceRange.from) > Math.floor(priceRange.min) || Math.floor(priceRange.to) < Math.floor(priceRange.max)); const shopOnlineFilterCount = $('.price-avail-ship .selected').length + (priceFilterActive ? 1 : 0); if (shopOnlineFilterCount > 0) { $('.price-avail-ship .filter-group-count').text(shopOnlineFilterCount).show(); } else { $('.price-avail-ship .filter-group-count').hide(); } const totalFilters = $('.filter-group .selected').length + (priceFilterActive ? 1 : 0); if (totalFilters > 0) { $('.apply-filters .total-filter-count').show().text(totalFilters); $('.toggle-filters .total-filter-count').show().text(totalFilters); } else { $('.apply-filters .total-filter-count').hide(); $('.toggle-filters .total-filter-count').hide(); } } /** * Update the active filters list according to the last selected or released filter. * Then pass the resulting array of cells to show to The Grid. * * @access Public. * @param int filterId: the filter id to toggle on or off. * @param bool activate: if we are adding a filter a releasing a filter. * @return void. */ self.updateFilters = function (filterId, activate) { var filteredCells = [], filteredProductIds = [], activeCategoryFiltersCount = 0, activeFeatureFiltersCount = 0, activePriceFilter = false; // Activate/Deactivate the current filter in the list of active filters. if (filterId) self.activeFilters[filterId] = activate; //----------------------- CATEGORIES -----------------------// // First loop through active filters to merge all the selected categories. for (var fid in self.activeFilters) { // Perform a OR filter on categories only. Then apply feature filters in a second loop. if (fid.indexOf('f') === -1 && self.activeFilters[fid]) { // Perform a "OR" filter (keep product if it matches this filter OR this one). filteredProductIds = filteredProductIds.concat(productsByFilters[fid]); activeCategoryFiltersCount++; } else if (fid.indexOf('f') > -1 && self.activeFilters[fid]) activeFeatureFiltersCount++; } // Remove dupes if at least 2 categories are selected. if (activeCategoryFiltersCount > 1) filteredProductIds = filteredProductIds.unique(); // Fallback to all the products if no category is selected. else if (!activeCategoryFiltersCount) filteredProductIds = productIdList.clone();// Clone not to affect original array. // console.log('filtered ids after categories loop:', filteredProductIds); //----------------------------------------------------------// //------------------------ FEATURES ------------------------// // Second loop through active filters to remove products if they are not found in EVERY active feature filter. if (activeFeatureFiltersCount) { var tmpArr = filteredProductIds.clone();// Clone not to affect original array. for (var fid in self.activeFilters) { if (fid.indexOf('f') > -1 && self.activeFilters[fid]) { for (var i = 0, l = filteredProductIds.length; i < l; i++) { if ((productsByFilters[fid] || []).indexOf(parseInt(filteredProductIds[i])) === -1) delete tmpArr[i]; } } } filteredProductIds = tmpArr.unique(); } //----------------------------------------------------------// //------------------------- PRICES -------------------------// // Third loop through filtered products to remove products out of current price range if price range is defined. activePriceFilter = priceRange.max && (priceRange.to < priceRange.max || priceRange.from > priceRange.min); if (activePriceFilter) { tmpArr = []; for (var i = 0, l = filteredProductIds.length; i < l; i++) { var price = productPrices[filteredProductIds[i]] || 0; if (price < priceRange.from || price > priceRange.to) { // console.log("removing product "+filteredProductIds[i]+' as its price of '+price+' is not in the range: ['+priceRange.from+','+priceRange.to+'].'); } else { tmpArr.push(filteredProductIds[i]); } } filteredProductIds = tmpArr; } //----------------------------------------------------------// $('.reset-filters')[activeCategoryFiltersCount || activeFeatureFiltersCount || activePriceFilter ? 'show' : 'hide'](); // Now the filtered cells are including all the active filters. filteredCells = filteredProductIds.length ? cellsWrapper.find('[data-productid=' + filteredProductIds.join('],[data-productid=') + ']') : []; // Apply sorting on selection if any sorting filter is active. if (self.activeSorting) filteredCells = self.applySorting(filteredCells); // Show or hide the no match message according to the number of matches. $('.no-match')[filteredCells.length ? 'hide' : 'show'](); // Show/hide the compare button depending on whether there are products that can be compared if ($('.can-compare').length > 0) { $('#compare-button-1')[filteredCells.length ? 'fadeIn' : 'fadeOut'](300); } // Show the price filter tag const priceFromLocalised = self.formatCurrency(global.store.currencyCode, priceRange.from, global.store.culture); const priceToLocalised = self.formatCurrency(global.store.currencyCode, priceRange.to, global.store.culture); const priceFilterTag = `
    ${res.price_range}: ${global.store.id === 29 ? 'NT' : ''}${priceFromLocalised}–${global.store.id === 29 ? 'NT' : ''}${priceToLocalised}
    `; const priceFilterTagExists = $('.filter-tags .filter-tag-price').length > 0; if (!priceFilterTagExists && activePriceFilter) { $('.filter-tags-wrapper').append(priceFilterTag); } else { $('.filter-tag-price').html(`${res.price_range}: $${priceRange.from}–$${priceRange.to}`); } if (priceRange.from === Math.floor(priceRange.min) && priceRange.to === Math.floor(priceRange.max)) { $('.filter-tags-wrapper') .find('.filter-tag-price') .remove(); } const hasTags = $(`.filter-tags-wrapper .filter-tag`).length > 0; if (hasTags) { $('.filter-tags .filter-label').show(); } else { $('.filter-tags .filter-label').hide(); } self.updateFilterCount(); // Finally give all the filtered and sorted cells to The Grid. // Grid redrawing is not performed here but ulteriorly from the method caller. self.filteredCells = filteredCells; //$('#category-grid-container').show(); $('#category-grid-container').removeClass('filters-open'); grid1.grid('filter', filteredCells, false, true); }; /** * Apply sorting to the current filtered cells. ONLY APPLY ON A JQUERY COLLECTION. * If done on an array then passing array to jquery will loose the specific order. * * @access Public. * @param {jQuery collection} filteredCells: the collection of cells to resort. * @return void. */ self.applySorting = function (filteredCells) { filteredCells = filteredCells === undefined ? self.filteredCells : filteredCells; var multiplier = self.activeSorting.order === 'desc' ? -1 : 1; // Sort by popular. if (self.activeSorting.sort === 'popular') { var tmp = cellsWrapper.find('.category-cell').not(filteredCells); // console.log(cells, cellsWrapper.not(filteredCells)) return cellsWrapper.find('.category-cell').not(tmp); } // Sort by price or date. return filteredCells.sort(function (a, b) { switch (self.activeSorting.sort) { case 'price': a = productPrices[$(a).data('productid')] || 0; b = productPrices[$(b).data('productid')] || 0; break; case 'newest': a = parseInt($(a).data('newflag')) || 0; b = parseInt($(b).data('newflag')) || 0; break; default: break; } return (a - b) * multiplier; }); }; /** * Reset ALL the filters and also tell The Grid. * * @access Public. * @return void. */ self.resetFilters = function () { // Reset active filters. self.activeFilters = {}; // reset price range selection. priceRange.from = Math.floor(priceRange.min); priceRange.to = Math.ceil(priceRange.max); // Reset active sorting. self.activeSorting = { sort: 'popular', order: 'asc' }; // reset the previously cached filtered cells. self.filteredCells = cells; // Visually uncheck all filters and visually reset price range. $(".filters a.filter.selected").removeClass('selected'); if ($("#priceslider").val() !== '') { $("#priceslider").slider("prc", 0, 100); } // Hide the no match message and reset filters button and show compare button. $('.no-match').hide(); $('.pl-top .reset-filters').removeClass('show'); if ($('.can-compare').length > 0) { $('#compare-button-1').fadeIn(300); } // Remove tags $('.filter-tags-wrapper .filter-tag').remove(); $('.filter-tags .filter-label').hide(); //$('#category-grid-container').show(); $('#category-grid-container').removeClass('filters-open'); // Tell The Grid to reset the collection of cells. grid1.grid('resetFilters'); // Scroll to top of the category grid. if ($(window).width() <= 640) { $([document.documentElement, document.body]).animate({ scrollTop: $(".pl-top").offset().top - 65 }, 100); } }; self.resetPriceFilter = function (e) { // reset price range selection. priceRange.from = priceRange.min; priceRange.to = priceRange.max; $("#priceslider").slider("prc", 0, 100); // Update filters and redraw grid self.updateFilters(); if(isLayout2024){ updateCategoryCells("pricefilter_reset",false) }else{ grid1.grid('redraw'); } // Remove tag $('.filter-tags-wrapper .filter-tag-price').remove(); // Remove label if there are no more tags const hasTags = $(`.filter-tags .filter-tag`).length > 0; if (hasTags) { $('.filter-tags-wrapper .filter-label').show(); } else { $('.filter-tags .filter-label').hide(); } } /** * Load the filters from URL if any. * Look for query string "filters=int,int,..." or "filters=int|int|..." according to AND or OR type filter. * So far on the site all the feature filters behave as a refinement (AND) e.g. Show cells that match this feature AND this one. * Now we want to allow a OR feature filter so we can do: Show cells that match this feature OR this one. * (So the matching products collection will be bigger). * * @access Private. * @param {array} filters: optional. If given will only loop among those filters. * They are given from the callback API to perform only the filters that could not be processed * before receiving API data. * @return void. */ loadFiltersFromUrl = function (filters) { postponedFiltersLoad = [];// Empty the class var from any previously treated filters. var filters = filters || (location.search.match(/filters=([^&]+)/) || ['', ''])[1].split(','); for (var i = 0, l = filters.length; i < l; i++) { var hasOrFeatureFilters = filters[i].indexOf('|') > -1; if (hasOrFeatureFilters) { // Check if the filter matches are available yet (case for category filters that don't need API data), // If not available postpone this filter treatment until the API callback. if (!productsByFilters.hasOwnProperty(filters[i].split('|')[0] + 'f')) { postponedFiltersLoad.push(filters[i]); continue; } var orFilters = filters[i].split('|'); // Create a virtual category including all the matches of each feature filter. ;) // For each feature filter that we want to combine, add the matches to a new virtual filter (merge arrays). for (var j = 0, m = orFilters.length; j < m; j++) { var fid = parseInt(orFilters[j]); if (fid && productsByFilters.hasOwnProperty(fid + 'f')) { productsByFilters[filters[i] + 'f'] = (productsByFilters[filters[i] + 'f'] || []).concat(productsByFilters[fid + 'f']); } } // After merging multiple matched product id lists, cleanup the duplicates. productsByFilters[filters[i] + 'f'] = productsByFilters[filters[i] + 'f'].unique(); // Set the new virtual filter as active. self.activeFilters[filters[i] + 'f'] = true; // Append the new created virtual feature filter to the filters pane to let user enable/disable the page preset filter. if (!$('.pre-applied').length) { $('.filters').prepend('
    ' + res.pre_applied_filter + '
    '); } } else { var fid = parseInt(filters[i]); // Check if the filter matches are available yet (case for category filters that don't need API data), // If not available postpone this filter treatment until the API callback. if (fid && (productsByFilters.hasOwnProperty(fid) || productsByFilters.hasOwnProperty(fid + 'f'))) { $('[data-filter="' + fid + '"]').addClass('selected'); fid = productsByFilters.hasOwnProperty(fid) ? fid : (fid + 'f'); self.activeFilters[fid] = true; } else postponedFiltersLoad.push(fid); } } if (!postponedFiltersLoad.length) { // Refresh the grid if no postponed filters. self.updateFilters(); if(isLayout2024){ updateCategoryCells("loadFiltersFromUrl",true) }else{ grid1.grid('redraw'); } } postponedFiltersLoad = filters.unique();// Wait the last loaded filters to refresh the grid (don't overload!). }; self.toggleTag = function (el, override) { const elem = el.tagName === 'A' ? el : el.parentElement; const filterId = elem.getAttribute('data-filter'); const tagText = elem.innerHTML.trim(); const tag = `
    ${tagText}
    `; const exists = $(`.filter-tags .filter-tag[data-filter=${filterId}]`).length > 0; if (($(elem).hasClass('selected') && !exists) || override) { $('.filter-tags-wrapper').append(tag); } else { $('.filter-tags').find(`div[data-filter=${filterId}]`).remove(); } const hasTags = $(`.filter-tags .filter-tag`).length > 0; if (hasTags) { $('.filter-tags .filter-label').show(); } else { $('.filter-tags .filter-label').hide(); } } self.preloadTags = function (e) { var filters = filters || (location.search.match(/filters=([^&]+)/) || ['', ''])[1].split(','); filters.forEach(f => { const el = $(`.filters a[data-filter=\'${f}\']`); if (el.get(0) !== undefined) self.toggleTag(el.get(0), true); }) } self.formatCurrency = function (currency, amount, culture) { const temp = new Intl.NumberFormat( global.store.culture, { style: 'currency', currency: global.store.currencyCode }).format(amount) .replace(/(\.|\,)00/, '') .replace('\u00A0', '') .replace('SGD', '$') .replace('HK', '') .replace('US', ''); if (culture.toLowerCase() === 'it-it') { formatted = temp.substr(temp.length - 1, 1) + temp.substr(0, temp.length - 1); } else if (culture.toLowerCase() === 'ja-jp') { formatted = temp.substr(1, temp.length) + '円'; } // else if (culture.toLowerCase() === 'el-gr') { // formatted = temp.substr(1, temp.length) + temp.substr(0, 1); // } else { formatted = temp; } return formatted; } /** * First init the events related to the filters. * * @access private. * [bindEvents description] * @return {[type]} [description] */ var bindEvents = function () { $('#category-wrapper').on('click', '.sliding-container.open .faded-overlay', function () { $('.toggle-filters').trigger('click'); }); // Left pane filters. $('.filters') .on('click', 'a.filter', function (e) { e.preventDefault(); var filterActive = !$(this).hasClass('selected'), isFeatureFilter = $(this).parents('.feature-filters').length; filterId = $(this).data('filter'); if ($(this).hasClass('hot-filter')) { // Enable/disable the hot filter. self.activeFilters[filterId + 'f'] = filterActive; // Update hot button in top bar. $('.pl-top .hot-filter')[filterActive ? 'addClass' : 'removeClass']('i-check-lt'); } $(this).toggleClass('selected'); self.toggleTag(e.target); self.updateFilterCount(e.target); self.updateFilters(filterId + (isFeatureFilter ? 'f' : ''), filterActive); if(!isLayout2024){ grid1.grid('redraw'); } //check all a.filter element, if all of them dun exist selected, run updateCategory() function if(isLayout2024 && isFilterEmpty() && document.querySelector('.sortby-dropdown').getAttribute("data-sort").includes("popular")){ updateCategoryCells("filter_click_empty",false); } }) .on('click', '.title', function (e) { e.preventDefault(); var closed = !$(this).parent('.filter-group').hasClass('closed'); $(this).parent('.filter-group')[closed ? 'addClass' : 'removeClass']('closed') .find('ul')[closed ? 'slideUp' : 'slideDown'](); // For the Shop Online filter (Price/Availability/Free Shipping) // Hide all the items below it also if ($(this).parent('.filter-group').attr('id') === 'price-filter') { $('#price-filter').siblings() .each((i, j) => { $(j).find('ul')[closed ? 'slideUp' : 'slideDown'](); }) } }) // Trigger hot filter on only if no current page filter is given by url. if (location.search.indexOf('filters=') === -1) $('.filter[data-filter="hot"]').trigger('click'); // Top grid controls (sort reset filters and hot filter). $('.sortby-dropdown').on('click', 'li a', function () { //update value on parent container $('.sortby-dropdown').attr({ "data-sort": $(this).data('sort'), "data-order": $(this).data('order') }); $(this).parents('.sortby-dropdown').find('span span').html(this.innerHTML); $(this).parents('ul').hide(); self.activeSorting = { sort: $(this).data('sort'), order: $(this).data('order') || 'asc' }; self.filteredCells = self.applySorting(); grid1.grid('filter', self.filteredCells, false, true); if(isLayout2024){ sortCategoryCells($(this).data('sort'),$(this).data('order') || 'asc'); }else{ grid1.grid('redraw'); } }) // Top reset filters button. $(".reset-filters, .reset-filters-mobile,.filters-wrapper .filter-reset").on('click', function (e) { // There is also a reset filters button in the no-match message. $(".pl-top .reset-filters").hide(); self.resetFilters(); if(isLayout2024){ updateCategoryCells("resetfilter_click",false) }else{ grid1.grid('redraw'); } if ($(e.target).hasClass('reset-filters-mobile')) { $('.sliding-container').toggleClass('open'); $('.filter-buttons-wrapper').toggleClass('open'); if(isLayout2024){ $('#category-grid-container #category-grid').removeClass('open-filter-menu'); $('#category-grid-container .btn-filter').toggleClass('selected'); } $('#global-footer').toggleClass('filters-open'); toggleChatWidget(); } $('.filter-group-count').text(0).hide(); $('.total-filter-count').text(0).hide(); if ($(window).width() <= 640) { window.scrollTo(0, 0); } }); // Top hot button filter. $(".pl-top .hot-filter").on('click', function (e) { e.preventDefault(); $('[data-filter="hot"]').trigger('click'); }); // Filter tags $('.filter-tags').on('click', '.filter-tag', (e) => { const isPriceFilter = $(e.target).hasClass('filter-tag-price'); if (isPriceFilter) { self.resetPriceFilter(); } else { const elem = e.target.tagName === 'DIV' ? e.target : e.target.parentElement; const filterId = elem.getAttribute('data-filter'); $(`.filters a[data-filter=\'${filterId}\']`).click(); } }) window.setTimeout(self.preloadTags, 500); if (isLayout2024) { let isSelected; const toggleElements = (condition) => { const elements = [ [".sliding-container, .filter-buttons-wrapper", "open"], ["#category-grid-container, #global-footer", "filters-open"], ["#category-grid", "open-filter-menu"], ["body > #all", "blur"], ["body > #all", "open-filter-menu"], ["html", "no-scroll"] ]; const screenWidth = window.innerWidth || document.documentElement.clientWidth; elements.forEach(([selectors, className]) => { if ((screenWidth < 768 || screenWidth > 1024) && (selectors === "body > #all" || selectors === "html")) { return; // Skip this iteration } [...document.querySelectorAll(selectors)].forEach(el => el.classList.toggle(className, condition)); }); }; const resetFilters = () => { document.querySelector("#category-grid-container .btn-filter").classList.remove("selected"); toggleElements(false); // Ensure all filters are closed }; const handleToggle = () => { isSelected = document.querySelector("#category-grid-container .btn-filter").classList.toggle("selected"); toggleElements(isSelected); }; // Filter button click event document.querySelector("#category-grid-container .btn-filter").addEventListener("click", handleToggle); // Closing filter when clicking outside [...document.querySelectorAll("#category-grid-container #category-grid, body > #all")].forEach(el => { el.addEventListener("click", function (e) { if (e.target.classList.contains("open-filter-menu")) handleToggle(); }); }); // Hamburger menu click event document.querySelector("#main-nav li > a.hamburger").addEventListener("click", resetFilters); // filter close button event document.querySelector("#category-filter-container .filter-close").addEventListener("click", handleToggle); } }; /** * Init the filters handler display the missing filters, do calls to APIs and trigger events. * * @access private. * [init description] * @return {[type]} [description] */ var init = function () { initCategoryFilters(); if ($('#category-wrapper').data('compare-api') !== undefined) initFeatureFilters(); bindEvents(); if (location.search.indexOf('filters=') > -1) loadFiltersFromUrl(); }(); }; Array.prototype.unique = function () { var u = {}, a = []; for (var i = 0, l = this.length; i < l; ++i) { if (u.hasOwnProperty(this[i]) || !this[i]) continue; a.push(this[i]); u[this[i]] = 1; } return a; }; Array.prototype.clone = function () { return this.slice(0) }; toggleChatWidget = function () { if ($('.sliding-container').hasClass('open')) { if (typeof zE !== 'undefined') zE('webWidget', 'hide'); $('#alert-box-toggle').hide(); } else { if (typeof zE !== 'undefined') zE('webWidget', 'show'); $('#alert-box-toggle').show(); } } let initCategory = function () { //=============================== GRID LAYOUT ===============================// var baseOptions = { cells: '.category-cell', cellHeight: function (g) { return ((g.gridWidth / g.options.cellsPerRow) * 262 / 400) + 100; }, updateGridHeight: true, animationDelay: 0, animationSpeed: 300, breakpoints: { 1199: { cellsPerRow: 3, }, 1023: { cellsPerRow: 2, }, 370: { cellsPerRow: 1, } } }; if (isLayout2024) { baseOptions.cellsPerRow = 3; baseOptions.updateGridHeight = false; } else { baseOptions.cellsPerRow = 5; baseOptions.breakpoints[1919] = { cellsPerRow: 5 }; baseOptions.breakpoints[1599] = { cellsPerRow: 4 }; } grid1 = $('#category-grid').grid(baseOptions); //===========================================================================// //=========================== FILTERS & SORTING =============================// // Instantiate categoryPage. catPage = new categoryPage(); $('.toggle-filters').on('click', function () { $('.sliding-container').toggleClass('open'); $('.filter-buttons-wrapper').toggleClass('open'); $('#category-grid-container').addClass('filters-open'); $('#global-footer').toggleClass('filters-open'); self.toggleChatWidget(); }); $('.apply-filters').on('click', function () { $('.sliding-container').toggleClass('open'); $('.filter-buttons-wrapper').toggleClass('open'); $('#category-grid-container').removeClass('filters-open'); $('#global-footer').toggleClass('filters-open'); if(isLayout2024){ $('#category-grid-container .btn-filter').removeClass('selected'); $('#category-grid-container #category-grid').removeClass('open-filter-menu'); updateCategoryCells("applyfilter_click", document.querySelectorAll('.filters a.filter.selected').length != 0) }else{ grid1.grid('redraw'); } self.toggleChatWidget(); if ($(window).width() <= 640) { $([document.documentElement, document.body]).animate({ scrollTop: $(".pl-top").offset().top - 65 }, 0); } }); // Add all the items in the core items array. $(".category-cell").each(function () { catPage.oriItems.push(this); }); // Init filters. fh = new filtersHandler(); if (catPage.fetchPrices) fetchPrices(); // Top sort dropdown. $(".sortby-dropdown,.sortby-dropdown ul").width($(".sortby-dropdown ul").width() + 60); $(".sortby-dropdown") .on("mouseover", function () { $(".sortby-dropdown ul").show() }) .on("mouseout", function () { $(".sortby-dropdown ul").hide() }); if (isLayout2024) { $(".sortby-dropdown").on("click", function (e) { e.stopPropagation(); $(".sortby-dropdown ul").toggle(); }); // click on other place close dropdown menu $(document).on("click", function () { $(".sortby-dropdown ul").hide(); }); } //===========================================================================// //============================== COMPARE PRODUCTS ===========================// if ($('#category-wrapper').data('compare-api') !== undefined) { // Initialize compare chart with given in page "compareChartParams". CompareChart.init(compareChartParams); compareProducts = CompareChart.getProduct(); } //===========================================================================// //=============================== Update Category Cell function =============// // Check the subcat group length // update the category cell (hide) and change the divider class if condition meet // different breakpoinmt will have different max Cells Per Group if(isLayout2024){ updateCategoryCells("initCategory",false); } //===========================================================================// };;