zoukankan      html  css  js  c++  java
  • way.js

       1 (function (root, factory) {
       2 
       3     if (typeof define === "function" && define.amd) {
       4         define(factory);
       5     } else if (typeof exports === "object") {
       6         module.exports = factory();
       7     } else {
       8         root.way = factory();
       9     }
      10 
      11 }(this, function () {
      12 
      13     "use strict";
      14 
      15     var way, w, tagPrefix = "way";
      16 
      17     //////////////////////////////
      18     // EVENT EMITTER DEFINITION //
      19     //////////////////////////////
      20 
      21     var EventEmitter = function () {
      22 
      23         this._watchers = {};
      24         this._watchersAll = {};
      25 
      26     };
      27 
      28     EventEmitter.prototype.constructor = EventEmitter;
      29 
      30     EventEmitter.prototype.watchAll = function (handler) {
      31 
      32         this._watchersAll = this._watchersAll || [];
      33         if (!_w.contains(this._watchersAll, handler)) { this._watchersAll.push(handler); }
      34 
      35     }
      36 
      37     EventEmitter.prototype.watch = function (selector, handler) {
      38 
      39         if (!this._watchers) { this._watchers = {}; }
      40         this._watchers[selector] = this._watchers[selector] || [];
      41         this._watchers[selector].push(handler);
      42 
      43     }
      44 
      45     EventEmitter.prototype.findWatcherDeps = function (selector) {
      46 
      47         // Go up to look for parent watchers
      48         // ex: if "some.nested.value" is the selector, it should also trigger for "some"
      49 
      50         var result = [];
      51         var watchers = _w.keys(this._watchers);
      52         watchers.forEach(function (watcher) {
      53             if (startsWith(selector, watcher)) { result.push(watcher); }
      54         });
      55         return result;
      56 
      57     }
      58 
      59     EventEmitter.prototype.emitChange = function (selector /* , arguments */) {
      60 
      61         if (!this._watchers) { this._watchers = {}; }
      62 
      63         var self = this;
      64 
      65         // Send data down to the local watchers
      66         var deps = self.findWatcherDeps(selector);
      67         deps.forEach(function (item) {
      68             if (self._watchers[item]) {
      69                 self._watchers[item].forEach(function (handler) {
      70                     handler.apply(self, [self.get(item)]);
      71                 });
      72             }
      73         });
      74 
      75         // Send data down to the global watchers
      76         if (!self._watchersAll || !_w.isArray(self._watchersAll)) { return; }
      77         self._watchersAll.forEach(function (watcher) {
      78             if (_w.isFunction(watcher)) { watcher.apply(self, [selector, self.get(selector)]); }
      79         });
      80 
      81     }
      82 
      83     ////////////////////
      84     // WAY DEFINITION //
      85     ////////////////////
      86 
      87     var WAY = function () {
      88 
      89         this.data = {};
      90         this._bindings = {};
      91         this.options = {
      92             persistent: true,
      93             timeoutInput: 50,
      94             timeoutDOM: 500
      95         };
      96 
      97     };
      98 
      99     // Inherit from EventEmitter
     100     WAY.prototype = Object.create(EventEmitter.prototype);
     101     WAY.constructor = WAY;
     102 
     103     //////////////////////////
     104     // DOM METHODS CHAINING //
     105     //////////////////////////
     106 
     107     WAY.prototype.dom = function (element) {
     108 
     109         this._element = w.dom(element).get(0);
     110         return this;
     111 
     112     };
     113 
     114     //////////////////////////////
     115     // DOM METHODS: DOM -> JSON //
     116     //////////////////////////////
     117 
     118     WAY.prototype.toStorage = function (options, element) {
     119 
     120         var self = this,
     121             element = element || self._element,
     122             options = options || self.dom(element).getOptions(),
     123             data = self.dom(element).toJSON(options),
     124             scope = self.dom(element).scope(),
     125             selector = scope ? scope + "." + options.data : options.data;
     126 
     127         if (options.readonly) { return false; }
     128         self.set(selector, data, options);
     129 
     130     }
     131 
     132     WAY.prototype.toJSON = function (options, element) {
     133 
     134         var self = this,
     135             element = element || self._element,
     136             data = self.dom(element).getValue(),
     137             options = options || self.dom(element).getOptions();
     138 
     139         if (_w.isArray(options.pick)) { data = selectNested(data, options.pick, true); }
     140         if (_w.isArray(options.omit)) { data = selectNested(data, options.omit, false); }
     141 
     142         return data;
     143 
     144     }
     145 
     146     //////////////////////////////
     147     // DOM METHODS: JSON -> DOM //
     148     //////////////////////////////
     149 
     150     WAY.prototype.fromStorage = function (options, element) {
     151 
     152         var self = this,
     153             element = element || self._element,
     154             options = options || self.dom(element).getOptions();
     155 
     156         if (options.writeonly) { return false; }
     157 
     158         var scope = self.dom(element).scope(),
     159                 selector = scope ? scope + "." + options.data : options.data,
     160                 data = self.get(selector);
     161 
     162         self.dom(element).fromJSON(data, options);
     163 
     164     }
     165 
     166     WAY.prototype.fromJSON = function (data, options, element) {
     167 
     168         var self = this,
     169                 element = element || self._element,
     170                 options = options || self.dom(element).getOptions();
     171 
     172         if (options.writeonly) { return false; }
     173 
     174         if (_w.isObject(data)) {
     175             if (_w.isArray(options.pick)) { data = selectNested(data, options.pick, true); }
     176             if (_w.isArray(options.omit)) { data = selectNested(data, options.omit, false); }
     177             var currentData = _w.isObject(self.dom(element).toJSON()) ? self.dom(element).toJSON() : {};
     178             data = _w.extend(currentData, data);
     179         }
     180 
     181         if (options.json) { data = _json.isStringified(data) ? data : _json.prettyprint(data); }
     182 
     183         self.dom(element).setValue(data, options);
     184 
     185     }
     186 
     187     /////////////////////////////////
     188     // DOM METHODS: GET - SET HTML //
     189     /////////////////////////////////
     190 
     191     WAY.prototype.getValue = function (element) {
     192 
     193         var self = this,
     194             element = element || self._element;
     195 
     196         var getters = {
     197             "SELECT": function () {
     198                 return w.dom(element).val();
     199             },
     200             "INPUT": function () {
     201                 var type = w.dom(element).type();
     202                 if (_w.contains(["text", "password"], type)) {
     203                     return w.dom(element).val();
     204                 }
     205                 if (_w.contains(["checkbox", "radio"], type)) {
     206                     return w.dom(element).prop("checked") ? w.dom(element).val() : null;
     207                 }
     208 
     209             },
     210             "TEXTAREA": function () {
     211                 return w.dom(element).val();
     212             }
     213         }
     214         var defaultGetter = function (a) {
     215             return w.dom(element).html();
     216         }
     217 
     218         var elementType = w.dom(element).get(0).tagName;
     219         var getter = getters[elementType] || defaultGetter;
     220         return getter();
     221 
     222     }
     223 
     224     WAY.prototype._transforms = {
     225         uppercase: function (data) {
     226             return _w.isString(data) ? data.toUpperCase() : data;
     227         },
     228         lowercase: function (data) {
     229             return _w.isString(data) ? data.toLowerCase() : data;
     230         },
     231         reverse: function (data) {
     232             return data && data.split && _w.isFunction(data.split) ? data.split("").reverse().join("") : data;
     233         }
     234     };
     235 
     236     WAY.prototype.registerTransform = function (name, transform) {
     237         var self = this;
     238         if (_w.isFunction(transform)) { self._transforms[name] = transform; }
     239     }
     240 
     241     WAY.prototype.setValue = function (data, options, element) {
     242 
     243         var self = this,
     244             element = element || self._element,
     245             options = options || self.dom(element).getOptions();
     246 
     247         options.transform = options.transform || [];
     248         options.transform.forEach(function (transformName) {
     249             var transform = self._transforms[transformName] || function (data) { return data };
     250             data = transform(data);
     251         });
     252 
     253         var setters = {
     254 
     255             "SELECT": function (a) {
     256                 w.dom(element).val(a);
     257             },
     258             "INPUT": function (a) {
     259                 if (!_w.isString(a)) { a = JSON.stringify(a); }
     260                 var type = w.dom(element).get(0).type;
     261                 if (_w.contains(["text", "password"], type)) {
     262                     w.dom(element).val(a || "");
     263                 }
     264                 if (_w.contains(["checkbox", "radio"], type)) {
     265                     if (a === w.dom(element).val()) {
     266                         w.dom(element).prop("checked", true);
     267                     } else {
     268                         w.dom(element).prop("checked", false);
     269                     }
     270                 }
     271             },
     272             "TEXTAREA": function (a) {
     273                 if (!_w.isString(a)) { a = JSON.stringify(a); }
     274                 w.dom(element).val(a || "");
     275             },
     276             "PRE": function (a) {
     277                 if (options.html) {
     278                     w.dom(element).html(a);
     279                 } else {
     280                     w.dom(element).text(a);
     281                 }
     282             },
     283             "IMG": function (a) {
     284 
     285                 if (!a) {
     286                     a = options.default || "";
     287                     w.dom(element).attr("src", a);
     288                     return false;
     289                 }
     290 
     291                 var isValidImageUrl = function (url, cb) {
     292                     w.dom(element).addClass("way-loading");
     293                     w.dom("img", {
     294                         src: url,
     295                         onerror: function () { cb(false); },
     296                         onload: function () { cb(true); }
     297                     });
     298                 }
     299 
     300                 isValidImageUrl(a, function (response) {
     301                     w.dom(element).removeClass("way-loading");
     302                     if (response) {
     303                         w.dom(element).removeClass("way-error").addClass("way-success");
     304                     } else {
     305                         if (a) {
     306                             w.dom(element).addClass("way-error");
     307                         } else {
     308                             w.dom(element).removeClass("way-error").removeClass("way-success");
     309                         }
     310                         a = options.default || "";
     311                     }
     312                     w.dom(element).attr("src", a);
     313                 });
     314 
     315             }
     316 
     317         }
     318         var defaultSetter = function (a) {
     319 
     320             if (options.html) {
     321                 w.dom(element).html(a);
     322             } else {
     323                 w.dom(element).text(a);
     324             }
     325 
     326         }
     327 
     328         var elementType = w.dom(element).get(0).tagName;
     329         var setter = setters[elementType] || defaultSetter;
     330         setter(data);
     331 
     332     }
     333 
     334     WAY.prototype.setDefault = function (force, options, element) {
     335 
     336         var self = this,
     337             element = element || self._element,
     338             force = force || false,
     339             options = options ? _w.extend(self.dom(element).getOptions(), options) : self.dom(element).getOptions();
     340 
     341         // Should we just set the default value in the DOM, or also in the datastore?
     342         if (!options.default) { return false; }
     343         if (force) {
     344             self.set(options.data, options.default, options);
     345         } else {
     346             self.dom(element).setValue(options.default, options);
     347         }
     348 
     349     }
     350 
     351     WAY.prototype.setDefaults = function () {
     352 
     353         var self = this,
     354             dataSelector = "[" + tagPrefix + "-default]";
     355 
     356         var elements = w.dom(dataSelector).get();
     357         for (var i in elements) {
     358             var element = elements[i],
     359                     options = self.dom(element).getOptions(),
     360                     selector = options.data || null,
     361                     data = selector ? self.get(selector) : null;
     362             if (!data) { self.dom(element).setDefault(); }
     363         }
     364 
     365     }
     366 
     367     /////////////////////////////////////
     368     // DOM METHODS: GET - SET BINDINGS //
     369     /////////////////////////////////////
     370 
     371     // Scans the DOM to look for new bindings
     372     WAY.prototype.registerBindings = function () {
     373 
     374         // Dealing with bindings removed from the DOM by just resetting all the bindings all the time.
     375         // Isn't there a better way?
     376         // One idea would be to add a "way-bound" class to bound elements
     377         // self._bindings = {};
     378 
     379         var self = this;
     380         var selector = "[" + tagPrefix + "-data]";
     381         self._bindings = {};
     382 
     383         var elements = w.dom(selector).get();
     384         for (var i in elements) {
     385             var element = elements[i],
     386                     options = self.dom(element).getOptions(),
     387                     scope = self.dom(element).scope(),
     388                     selector = scope ? scope + "." + options.data : options.data;
     389 
     390             self._bindings[selector] = self._bindings[selector] || [];
     391             if (!_w.contains(self._bindings[selector], w.dom(element).get(0))) {
     392                 self._bindings[selector].push(w.dom(element).get(0));
     393             }
     394 
     395         }
     396 
     397     }
     398 
     399     WAY.prototype.updateBindings = function (selector) {
     400 
     401         var self = this;
     402         self._bindings = self._bindings || {};
     403 
     404         // Set bindings for the data selector
     405         var bindings = pickAndMergeParentArrays(self._bindings, selector);
     406         bindings.forEach(function (element) {
     407             var focused = (w.dom(element).get(0) === w.dom(":focus").get(0)) ? true : false;
     408             if (!focused) { self.dom(element).fromStorage(); }
     409         });
     410 
     411         // Set bindings for the global selector
     412         if (self._bindings["__all__"]) {
     413             self._bindings["__all__"].forEach(function (element) {
     414                 self.dom(element).fromJSON(self.data);
     415             });
     416         }
     417 
     418     }
     419 
     420     ////////////////////////////////////
     421     // DOM METHODS: GET - SET REPEATS //
     422     ////////////////////////////////////
     423 
     424     WAY.prototype.registerRepeats = function () {
     425 
     426         // Register repeats
     427         var self = this;
     428         var selector = "[" + tagPrefix + "-repeat]";
     429         self._repeats = self._repeats || {};
     430         self._repeatsCount = self._repeatsCount || 0;
     431 
     432         var elements = w.dom(selector).get();
     433         for (var i in elements) {
     434             var element = elements[i],
     435                     options = self.dom(element).getOptions();
     436 
     437             self._repeats[options.repeat] = self._repeats[options.repeat] || [];
     438 
     439             var wrapperAttr = tagPrefix + "-repeat-wrapper="" + self._repeatsCount + """,
     440                     parent = w.dom(element).parent("[" + wrapperAttr + "]");
     441             if (!parent.length) {
     442 
     443                 self._repeats[options.repeat].push({
     444                     id: self._repeatsCount,
     445                     element: w.dom(element).clone(true).removeAttr(tagPrefix + "-repeat").removeAttr(tagPrefix + "-filter").get(0),
     446                     selector: options.repeat,
     447                     filter: options.filter
     448                 });
     449 
     450                 var wrapper = document.createElement("div");
     451                 w.dom(wrapper).attr(tagPrefix + "-repeat-wrapper", self._repeatsCount);
     452                 w.dom(wrapper).attr(tagPrefix + "-scope", options.repeat);
     453                 if (options.filter) { w.dom(wrapper).attr(tagPrefix + "-filter", options.filter); }
     454 
     455                 w.dom(element).replaceWith(wrapper);
     456                 self.updateRepeats(options.repeat);
     457 
     458                 self._repeatsCount++;
     459 
     460             }
     461 
     462         }
     463 
     464     }
     465 
     466     /*
     467     WAY.prototype._filters = {
     468         noFalsy: function(item ) {
     469             if (!item) {
     470                 return false;
     471             } else {
     472                 return true;
     473             }
     474         }
     475     };
     476 
     477     WAY.prototype.registerFilter = function(name, filter) {
     478         var self = this;
     479         if (_w.isFunction(filter)) { self._filters[name] = filter; }
     480     }
     481     */
     482 
     483     WAY.prototype.updateRepeats = function (selector) {
     484 
     485         var self = this;
     486         self._repeats = self._repeats || {};
     487 
     488         var repeats = pickAndMergeParentArrays(self._repeats, selector);
     489 
     490         repeats.forEach(function (repeat) {
     491 
     492             var wrapper = "[" + tagPrefix + "-repeat-wrapper="" + repeat.id + ""]",
     493                     data = self.get(repeat.selector),
     494                     items = [];
     495 
     496             repeat.filter = repeat.filter || [];
     497             w.dom(wrapper).empty();
     498 
     499             for (var key in data) {
     500 
     501                 /*
     502                 var item = data[key],
     503                         test = true;
     504                 for (var i in repeat.filter) {
     505                     var filterName = repeat.filter[i];
     506                     var filter = self._filters[filterName] || function(data) { return data };
     507                     test = filter(item);
     508                     if (!test) { break; }
     509                 }
     510                 if (!test) { continue; }
     511                 */
     512 
     513                 w.dom(repeat.element).attr(tagPrefix + "-scope", key);
     514                 var html = w.dom(repeat.element).get(0).outerHTML;
     515                 html = html.replace(/$$key/gi, key);
     516                 items.push(html);
     517 
     518             }
     519 
     520             w.dom(wrapper).html(items.join(""));
     521             self.registerBindings();
     522             self.updateBindings();
     523 
     524         });
     525 
     526     }
     527 
     528     ////////////////////////
     529     // DOM METHODS: FORMS //
     530     ////////////////////////
     531 
     532     WAY.prototype.updateForms = function () {
     533 
     534         // If we just parse the forms with form2js (see commits before 08/19/2014) and set the data with way.set(),
     535         // we reset the entire data for this pathkey in the datastore. It causes the bug
     536         // reported here: https://github.com/gwendall/way.js/issues/10
     537         // Solution:
     538         // 1. watch new forms with a [way-data] attribute
     539         // 2. remove this attribute
     540         // 3. attach the appropriate attributes to its child inputs
     541         // -> so that each input is set separately to way.js' datastore
     542 
     543         var self = this;
     544         var selector = "form[" + tagPrefix + "-data]";
     545 
     546         var elements = w.dom(selector).get();
     547         for (var i in elements) {
     548 
     549             var form = elements[i],
     550                     options = self.dom(form).getOptions(),
     551                     formDataSelector = options.data;
     552             w.dom(form).removeAttr(tagPrefix + "-data");
     553 
     554             // Reverse needed to set the right index for "[]" names
     555             var inputs = w.dom(form).find("[name]").reverse().get();
     556             for (var i in inputs) {
     557 
     558                 var input = inputs[i],
     559                         name = w.dom(input).attr("name");
     560 
     561                 if (endsWith(name, "[]")) {
     562                     var array = name.split("[]")[0],
     563                             arraySelector = "[name^='" + array + "']",
     564                             arrayIndex = w.dom(form).find(arraySelector).get().length;
     565                     name = array + "." + arrayIndex;
     566                 }
     567                 var selector = formDataSelector + "." + name;
     568                 options.data = selector;
     569                 self.dom(input).setOptions(options);
     570                 w.dom(input).removeAttr("name");
     571 
     572             }
     573 
     574         }
     575 
     576     }
     577 
     578     /////////////////////////////////////////////
     579     // DOM METHODS: GET - SET ALL DEPENDENCIES //
     580     /////////////////////////////////////////////
     581 
     582     WAY.prototype.registerDependencies = function () {
     583 
     584         this.registerBindings();
     585         this.registerRepeats();
     586 
     587     }
     588 
     589     WAY.prototype.updateDependencies = function (selector) {
     590 
     591         this.updateBindings(selector);
     592         this.updateRepeats(selector);
     593         this.updateForms(selector);
     594 
     595     }
     596 
     597     //////////////////////////////////
     598     // DOM METHODS: OPTIONS PARSING //
     599     //////////////////////////////////
     600 
     601     WAY.prototype.setOptions = function (options, element) {
     602 
     603         var self = this,
     604                 element = self._element || element;
     605 
     606         for (var k in options) {
     607             var attr = tagPrefix + "-" + k,
     608                     value = options[k];
     609             w.dom(element).attr(attr, value);
     610         }
     611 
     612     }
     613 
     614     WAY.prototype.getOptions = function (element) {
     615 
     616         var self = this,
     617             element = element || self._element,
     618             defaultOptions = {
     619                 data: null,
     620                 html: false,
     621                 readonly: false,
     622                 writeonly: false,
     623                 persistent: false
     624             };
     625         return _w.extend(defaultOptions, self.dom(element).getAttrs(tagPrefix));
     626 
     627     }
     628 
     629     WAY.prototype.getAttrs = function (prefix, element) {
     630 
     631         var self = this,
     632                 element = element || self._element;
     633 
     634         var parseAttrValue = function (key, value) {
     635 
     636             var attrTypes = {
     637                 pick: "array",
     638                 omit: "array",
     639                 readonly: "boolean",
     640                 writeonly: "boolean",
     641                 json: "boolean",
     642                 html: "boolean",
     643                 persistent: "boolean"
     644             };
     645 
     646             var parsers = {
     647                 array: function (value) {
     648                     return value.split(",");
     649                 },
     650                 boolean: function (value) {
     651                     if (value === "true") { return true; }
     652                     if (value === "false") { return false; }
     653                     return true;
     654                 }
     655             };
     656             var defaultParser = function () { return value; };
     657             var valueType = attrTypes[key] || null;
     658             var parser = parsers[valueType] || defaultParser;
     659 
     660             return parser(value);
     661 
     662         }
     663 
     664         var attributes = {};
     665         var attrs = [].slice.call(w.dom(element).get(0).attributes);
     666         attrs.forEach(function (attr) {
     667             var include = (prefix && startsWith(attr.name, prefix + "-")) ? true : false;
     668             if (include) {
     669                 var name = (prefix) ? attr.name.slice(prefix.length + 1, attr.name.length) : attr.name;
     670                 var value = parseAttrValue(name, attr.value);
     671                 if (_w.contains(["transform", "filter"], name)) { value = value.split("|"); }
     672                 attributes[name] = value;
     673             }
     674         });
     675 
     676         return attributes;
     677 
     678     }
     679 
     680     //////////////////////////
     681     // DOM METHODS: SCOPING //
     682     //////////////////////////
     683 
     684     WAY.prototype.scope = function (options, element) {
     685 
     686         var self = this,
     687                 element = element || self._element,
     688                 scopeAttr = tagPrefix + "-scope",
     689                 scopeBreakAttr = tagPrefix + "-scope-break",
     690                 scopes = [],
     691                 scope = "";
     692 
     693         var parentsSelector = "[" + scopeBreakAttr + "], [" + scopeAttr + "]";
     694         var elements = w.dom(element).parents(parentsSelector).get();
     695         for (var i in elements) {
     696             var el = elements[i];
     697             if (w.dom(el).attr(scopeBreakAttr)) { break; }
     698             var attr = w.dom(el).attr(scopeAttr);
     699             scopes.unshift(attr);
     700         }
     701         if (w.dom(element).attr(scopeAttr)) { scopes.push(w.dom(element).attr(scopeAttr)); }
     702         if (w.dom(element).attr(scopeBreakAttr)) { scopes = []; }
     703 
     704         scope = _w.compact(scopes).join(".");
     705 
     706         return scope;
     707 
     708     }
     709 
     710     //////////////////
     711     // DATA METHODS //
     712     //////////////////
     713 
     714     WAY.prototype.get = function (selector) {
     715 
     716         var self = this;
     717         if (selector !== undefined && !_w.isString(selector)) { return false; }
     718         if (!self.data) { return {}; }
     719         return selector ? _json.get(self.data, selector) : self.data;
     720 
     721     }
     722 
     723     WAY.prototype.set = function (selector, value, options) {
     724 
     725         if (!selector) { return false; }
     726         if (selector.split(".")[0] === "this") {
     727             console.log("Sorry, "this" is a reserved word in way.js");
     728             return false;
     729         }
     730 
     731         var self = this;
     732         options = options || {};
     733 
     734         if (selector) {
     735 
     736             if (!_w.isString(selector)) { return false; }
     737             self.data = self.data || {};
     738             self.data = selector ? _json.set(self.data, selector, value) : {};
     739 
     740             self.updateDependencies(selector);
     741             self.emitChange(selector, value);
     742             if (options.persistent) { self.backup(selector); }
     743         }
     744 
     745     }
     746 
     747     WAY.prototype.push = function (selector, value, options) {
     748 
     749         if (!selector) { return false; }
     750 
     751         var self = this;
     752         options = options || {};
     753 
     754         if (selector) {
     755             self.data = selector ? _json.push(self.data, selector, value, true) : {};
     756         }
     757 
     758         self.updateDependencies(selector);
     759         self.emitChange(selector, null);
     760         if (options.persistent) { self.backup(selector); }
     761 
     762     }
     763 
     764     WAY.prototype.remove = function (selector, options) {
     765 
     766         var self = this;
     767         options = options || {};
     768 
     769         if (selector) {
     770             self.data = _json.remove(self.data, selector);
     771         } else {
     772             self.data = {};
     773         }
     774 
     775         self.updateDependencies(selector);
     776         self.emitChange(selector, null);
     777         if (options.persistent) { self.backup(selector); }
     778 
     779     }
     780 
     781     WAY.prototype.clear = function () {
     782 
     783         this.remove(null, { persistent: true });
     784 
     785     }
     786 
     787     //////////////////////////
     788     // LOCALSTORAGE METHODS //
     789     //////////////////////////
     790 
     791     WAY.prototype.backup = function () {
     792 
     793         var self = this;
     794         if (!self.options.persistent) { return; }
     795         try {
     796             var data = self.data || {};
     797             localStorage.setItem(tagPrefix, JSON.stringify(data));
     798         } catch (e) {
     799             console.log("Your browser does not support localStorage.");
     800         }
     801 
     802     }
     803 
     804     WAY.prototype.restore = function () {
     805 
     806         var self = this;
     807         if (!self.options.persistent) { return; }
     808         try {
     809             var data = localStorage.getItem(tagPrefix);
     810             try {
     811                 data = JSON.parse(data);
     812                 for (var key in data) {
     813                     self.set(key, data[key]);
     814                 }
     815             } catch (e) { }
     816         } catch (e) {
     817             console.log("Your browser does not support localStorage.");
     818         }
     819 
     820     }
     821 
     822     //////////
     823     // MISC //
     824     //////////
     825 
     826     var matchesSelector = function (el, selector) {
     827         var matchers = ["matches", "matchesSelector", "webkitMatchesSelector", "mozMatchesSelector", "msMatchesSelector", "oMatchesSelector"],
     828                 fn = null;
     829         for (var i in matchers) {
     830             fn = matchers[i];
     831             if (_w.isFunction(el[fn])) {
     832                 return el[fn](selector);
     833             }
     834         }
     835         return false;
     836     }
     837 
     838     var startsWith = function (str, starts) {
     839 
     840         if (starts === "") { return true; }
     841         if (str === null || starts === null) { return false; }
     842         str = String(str); starts = String(starts);
     843         return str.length >= starts.length && str.slice(0, starts.length) === starts;
     844 
     845     }
     846 
     847     var endsWith = function (str, ends) {
     848 
     849         if (ends === "") { return true; }
     850         if (str === null || ends === null) { return false; }
     851         str = String(str); ends = String(ends);
     852         return str.length >= ends.length && str.slice(str.length - ends.length, str.length) === ends;
     853 
     854     }
     855 
     856     var cleanEmptyKeys = function (object) {
     857 
     858         return _w.pick(object, _w.compact(_w.keys(object)));
     859 
     860     }
     861 
     862     var filterStartingWith = function (object, string, type) { // true: pick - false: omit
     863 
     864         var keys = _w.keys(object);
     865         keys.forEach(function (key) {
     866             if (type) {
     867                 if (!startsWith(key, string)) { delete object[key]; }
     868             } else {
     869                 if (startsWith(key, string)) { delete object[key]; }
     870             }
     871         });
     872         return object;
     873 
     874     }
     875 
     876     var selectNested = function (data, keys, type) { // true: pick - false: omit
     877 
     878         // Flatten / unflatten to allow for nested picks / omits (doesn't work with regular pick)
     879         // ex:  data = {something:{nested:"value"}}
     880         //        keys = ['something.nested']
     881 
     882         var flat = _json.flatten(data);
     883         for (var i in keys) flat = filterStartingWith(flat, keys[i], type);
     884         var unflat = _json.unflatten(flat);
     885         // Unflatten returns an object with an empty property if it is given an empty object
     886         return cleanEmptyKeys(unflat);
     887 
     888     }
     889 
     890     var pickAndMergeParentArrays = function (object, selector) {
     891 
     892         // Example:
     893         // object = { a: [1,2,3], a.b: [4,5,6], c: [7,8,9] }
     894         // fn(object, "a.b")
     895         // > [1,2,3,4,5,6]
     896 
     897         var keys = [];
     898         if (selector) {
     899 
     900             // Set bindings for the specified selector
     901 
     902             // (bindings that are repeat items)
     903             var split = selector.split("."),
     904                     lastKey = split[split.length - 1],
     905                     isArrayItem = !isNaN(lastKey);
     906 
     907             if (isArrayItem) {
     908                 split.pop();
     909                 var key = split.join(".");
     910                 keys = object[key] ? _w.union(keys, object[key]) : keys;
     911             }
     912 
     913             // (bindings with keys starting with, to include nested bindings)
     914             for (var key in object) {
     915                 if (startsWith(key, selector)) { keys = _w.union(keys, object[key]); }
     916             }
     917 
     918         } else {
     919 
     920             // Set bindings for all selectors
     921             for (var key in object) {
     922                 keys = _w.union(keys, object[key]);
     923             }
     924 
     925         }
     926         return keys;
     927 
     928     }
     929 
     930     var isPrintableKey = function (e) {
     931 
     932         var keycode = e.keyCode;
     933         if (!keycode) { return true; }
     934 
     935         var valid =
     936             (keycode === 8) || // delete
     937             (keycode > 47 && keycode < 58) || // number keys
     938             keycode === 32 || keycode === 13 || // spacebar & return key(s) (if you want to allow carriage returns)
     939             (keycode > 64 && keycode < 91) || // letter keys
     940             (keycode > 95 && keycode < 112) || // numpad keys
     941             (keycode > 185 && keycode < 193) || // ;=,-./` (in order)
     942             (keycode > 218 && keycode < 223);   // []' (in order)
     943 
     944         return valid;
     945 
     946     }
     947 
     948     var escapeHTML = function (str) {
     949         return str && _w.isString(str) ? str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;") : str;
     950     }
     951 
     952     ///////////////////////////////////////////////////
     953     // _w (strip of the required underscore methods) //
     954     ///////////////////////////////////////////////////
     955 
     956     var _w = {};
     957 
     958     var ArrayProto = Array.prototype,
     959             ObjProto = Object.prototype,
     960             FuncProto = Function.prototype;
     961 
     962     var nativeIsArray = Array.isArray,
     963             nativeKeys = Object.keys,
     964             nativeBind = FuncProto.bind;
     965 
     966     var
     967         push = ArrayProto.push,
     968         slice = ArrayProto.slice,
     969         concat = ArrayProto.concat,
     970         toString = ObjProto.toString,
     971         hasOwnProperty = ObjProto.hasOwnProperty;
     972 
     973     var flatten = function (input, shallow, strict, output) {
     974         if (shallow && _w.every(input, _w.isArray)) {
     975             return concat.apply(output, input);
     976         }
     977         for (var i = 0, length = input.length; i < length; i++) {
     978             var value = input[i];
     979             if (!_w.isArray(value) && !_w.isArguments(value)) {
     980                 if (!strict) output.push(value);
     981             } else if (shallow) {
     982                 push.apply(output, value);
     983             } else {
     984                 flatten(value, shallow, strict, output);
     985             }
     986         }
     987         return output;
     988     };
     989 
     990     var createCallback = function (func, context, argCount) {
     991         if (context === void 0) return func;
     992         switch (argCount == null ? 3 : argCount) {
     993             case 1: return function (value) {
     994                 return func.call(context, value);
     995             };
     996             case 2: return function (value, other) {
     997                 return func.call(context, value, other);
     998             };
     999             case 3: return function (value, index, collection) {
    1000                 return func.call(context, value, index, collection);
    1001             };
    1002             case 4: return function (accumulator, value, index, collection) {
    1003                 return func.call(context, accumulator, value, index, collection);
    1004             };
    1005         }
    1006         return function () {
    1007             return func.apply(context, arguments);
    1008         };
    1009     };
    1010 
    1011     _w.compact = function (array) {
    1012         return _w.filter(array, _w.identity);
    1013     };
    1014 
    1015     _w.filter = function (obj, predicate, context) {
    1016         var results = [];
    1017         if (obj == null) return results;
    1018         predicate = _w.iteratee(predicate, context);
    1019         _w.each(obj, function (value, index, list) {
    1020             if (predicate(value, index, list)) results.push(value);
    1021         });
    1022         return results;
    1023     };
    1024 
    1025     _w.identity = function (value) {
    1026         return value;
    1027     };
    1028 
    1029     _w.every = function (obj, predicate, context) {
    1030         if (obj == null) return true;
    1031         predicate = _w.iteratee(predicate, context);
    1032         var keys = obj.length !== +obj.length && _w.keys(obj),
    1033                 length = (keys || obj).length,
    1034                 index, currentKey;
    1035         for (index = 0; index < length; index++) {
    1036             currentKey = keys ? keys[index] : index;
    1037             if (!predicate(obj[currentKey], currentKey, obj)) return false;
    1038         }
    1039         return true;
    1040     };
    1041 
    1042     _w.union = function () {
    1043         return _w.uniq(flatten(arguments, true, true, []));
    1044     };
    1045 
    1046     _w.uniq = function (array, isSorted, iteratee, context) {
    1047         if (array == null) return [];
    1048         if (!_w.isBoolean(isSorted)) {
    1049             context = iteratee;
    1050             iteratee = isSorted;
    1051             isSorted = false;
    1052         }
    1053         if (iteratee != null) iteratee = _w.iteratee(iteratee, context);
    1054         var result = [];
    1055         var seen = [];
    1056         for (var i = 0, length = array.length; i < length; i++) {
    1057             var value = array[i];
    1058             if (isSorted) {
    1059                 if (!i || seen !== value) result.push(value);
    1060                 seen = value;
    1061             } else if (iteratee) {
    1062                 var computed = iteratee(value, i, array);
    1063                 if (_w.indexOf(seen, computed) < 0) {
    1064                     seen.push(computed);
    1065                     result.push(value);
    1066                 }
    1067             } else if (_w.indexOf(result, value) < 0) {
    1068                 result.push(value);
    1069             }
    1070         }
    1071         return result;
    1072     };
    1073 
    1074     _w.pick = function (obj, iteratee, context) {
    1075         var result = {}, key;
    1076         if (obj == null) return result;
    1077         if (_w.isFunction(iteratee)) {
    1078             iteratee = createCallback(iteratee, context);
    1079             for (key in obj) {
    1080                 var value = obj[key];
    1081                 if (iteratee(value, key, obj)) result[key] = value;
    1082             }
    1083         } else {
    1084             var keys = concat.apply([], slice.call(arguments, 1));
    1085             obj = new Object(obj);
    1086             for (var i = 0, length = keys.length; i < length; i++) {
    1087                 key = keys[i];
    1088                 if (key in obj) result[key] = obj[key];
    1089             }
    1090         }
    1091         return result;
    1092     };
    1093 
    1094     _w.has = function (obj, key) {
    1095         return obj != null && hasOwnProperty.call(obj, key);
    1096     };
    1097 
    1098     _w.keys = function (obj) {
    1099         if (!_w.isObject(obj)) return [];
    1100         if (nativeKeys) return nativeKeys(obj);
    1101         var keys = [];
    1102         for (var key in obj) if (_w.has(obj, key)) keys.push(key);
    1103         return keys;
    1104     };
    1105 
    1106     _w.contains = function (obj, target) {
    1107         if (obj == null) return false;
    1108         if (obj.length !== +obj.length) obj = _w.values(obj);
    1109         return _w.indexOf(obj, target) >= 0;
    1110     };
    1111 
    1112     _w.sortedIndex = function (array, obj, iteratee, context) {
    1113         iteratee = _w.iteratee(iteratee, context, 1);
    1114         var value = iteratee(obj);
    1115         var low = 0, high = array.length;
    1116         while (low < high) {
    1117             var mid = low + high >>> 1;
    1118             if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
    1119         }
    1120         return low;
    1121     };
    1122 
    1123     _w.property = function (key) {
    1124         return function (obj) {
    1125             return obj[key];
    1126         };
    1127     };
    1128 
    1129     _w.iteratee = function (value, context, argCount) {
    1130         if (value == null) return _w.identity;
    1131         if (_w.isFunction(value)) return createCallback(value, context, argCount);
    1132         if (_w.isObject(value)) return _w.matches(value);
    1133         return _w.property(value);
    1134     };
    1135 
    1136     _w.pairs = function (obj) {
    1137         var keys = _w.keys(obj);
    1138         var length = keys.length;
    1139         var pairs = Array(length);
    1140         for (var i = 0; i < length; i++) {
    1141             pairs[i] = [keys[i], obj[keys[i]]];
    1142         }
    1143         return pairs;
    1144     };
    1145 
    1146     _w.matches = function (attrs) {
    1147         var pairs = _w.pairs(attrs), length = pairs.length;
    1148         return function (obj) {
    1149             if (obj == null) return !length;
    1150             obj = new Object(obj);
    1151             for (var i = 0; i < length; i++) {
    1152                 var pair = pairs[i], key = pair[0];
    1153                 if (pair[1] !== obj[key] || !(key in obj)) return false;
    1154             }
    1155             return true;
    1156         };
    1157     };
    1158 
    1159     _w.indexOf = function (array, item, isSorted) {
    1160         if (array == null) return -1;
    1161         var i = 0, length = array.length;
    1162         if (isSorted) {
    1163             if (typeof isSorted == 'number') {
    1164                 i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
    1165             } else {
    1166                 i = _w.sortedIndex(array, item);
    1167                 return array[i] === item ? i : -1;
    1168             }
    1169         }
    1170         for (; i < length; i++) if (array[i] === item) return i;
    1171         return -1;
    1172     };
    1173 
    1174     _w.values = function (obj) {
    1175         var keys = _w.keys(obj);
    1176         var length = keys.length;
    1177         var values = Array(length);
    1178         for (var i = 0; i < length; i++) {
    1179             values[i] = obj[keys[i]];
    1180         }
    1181         return values;
    1182     };
    1183 
    1184     _w.extend = function (obj) {
    1185         if (!_w.isObject(obj)) return obj;
    1186         var source, prop;
    1187         for (var i = 1, length = arguments.length; i < length; i++) {
    1188             source = arguments[i];
    1189             for (prop in source) {
    1190                 if (hasOwnProperty.call(source, prop)) {
    1191                     obj[prop] = source[prop];
    1192                 }
    1193             }
    1194         }
    1195         return obj;
    1196     };
    1197 
    1198     _w.isArray = function (obj) {
    1199         return toString.call(obj) === '[object Array]';
    1200     };
    1201 
    1202     _w.isBoolean = function (obj) {
    1203         return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
    1204     };
    1205 
    1206     _w.isUndefined = function (obj) {
    1207         return obj === void 0;
    1208     };
    1209 
    1210     _w.isObject = function (obj) {
    1211         var type = typeof obj;
    1212         return type === 'function' || type === 'object' && !!obj;
    1213     };
    1214 
    1215     _w.each = function (obj, iteratee, context) {
    1216         if (obj == null) return obj;
    1217         iteratee = createCallback(iteratee, context);
    1218         var i, length = obj.length;
    1219         if (length === +length) {
    1220             for (i = 0; i < length; i++) {
    1221                 iteratee(obj[i], i, obj);
    1222             }
    1223         } else {
    1224             var keys = _w.keys(obj);
    1225             for (i = 0, length = keys.length; i < length; i++) {
    1226                 iteratee(obj[keys[i]], keys[i], obj);
    1227             }
    1228         }
    1229         return obj;
    1230     };
    1231 
    1232     _w.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function (name) {
    1233         _w['is' + name] = function (obj) {
    1234             return toString.call(obj) === '[object ' + name + ']';
    1235         };
    1236     });
    1237 
    1238     ///////////////////////////////////////////////////////////
    1239     // _json (strip of the required underscore.json methods) //
    1240     ///////////////////////////////////////////////////////////
    1241 
    1242     var deepJSON = function (obj, key, value, remove) {
    1243 
    1244         var keys = key.replace(/[(["']?)([^1]+?)1?]/g, '.$2').replace(/^./, '').split('.'),
    1245                 root,
    1246                 i = 0,
    1247                 n = keys.length;
    1248 
    1249         // Set deep value
    1250         if (arguments.length > 2) {
    1251 
    1252             root = obj;
    1253             n--;
    1254 
    1255             while (i < n) {
    1256                 key = keys[i++];
    1257                 obj = obj[key] = _w.isObject(obj[key]) ? obj[key] : {};
    1258             }
    1259 
    1260             if (remove) {
    1261                 if (_w.isArray(obj)) {
    1262                     obj.splice(keys[i], 1);
    1263                 } else {
    1264                     delete obj[keys[i]];
    1265                 }
    1266             } else {
    1267                 obj[keys[i]] = value || "";
    1268             }
    1269 
    1270             value = root;
    1271 
    1272             // Get deep value
    1273         } else {
    1274             while ((obj = obj[keys[i++]]) != null && i < n) { };
    1275             value = i < n ? void 0 : obj;
    1276         }
    1277         if (value == null) {
    1278             value = "";
    1279         }
    1280         return value;
    1281 
    1282     }
    1283 
    1284     var _json = {}
    1285 
    1286     _json.VERSION = '0.1.0';
    1287     _json.debug = true;
    1288 
    1289     _json.exit = function (source, reason, data, value) {
    1290 
    1291         if (!_json.debug) return;
    1292 
    1293         var messages = {};
    1294         messages.noJSON = "Not a JSON";
    1295         messages.noString = "Not a String";
    1296         messages.noArray = "Not an Array";
    1297         messages.missing = "Missing argument";
    1298 
    1299         var error = { source: source, data: data, value: value };
    1300         error.message = messages[reason] ? messages[reason] : "No particular reason";
    1301         console.log("Error", error);
    1302         return;
    1303 
    1304     }
    1305 
    1306     _json.is = function (json) {
    1307 
    1308         return (toString.call(json) == "[object Object]");
    1309 
    1310     }
    1311 
    1312     _json.isStringified = function (string) {
    1313 
    1314         var test = false;
    1315         try {
    1316             test = /^[],:{}s]*$/.test(string.replace(/\["\/bfnrtu]/g, '@').
    1317             replace(/"[^"\
    
    ]*"|true|false|null|-?d+(?:.d*)?(?:[eE][+-]?d+)?/g, ']').
    1318             replace(/(?:^|:|,)(?:s*[)+/g, ''));
    1319         } catch (e) { }
    1320         return test;
    1321 
    1322     }
    1323 
    1324     _json.get = function (json, selector) {
    1325 
    1326         if (json == undefined) return _json.exit("get", "missing", "json", json);
    1327         if (selector == undefined) return _json.exit("get", "missing", "selector", selector);
    1328         if (!_w.isString(selector)) return _json.exit("get", "noString", "selector", selector);
    1329         return deepJSON(json, selector);
    1330 
    1331     };
    1332 
    1333     _json.set = function (json, selector, value) {
    1334 
    1335         if (json == undefined) return _json.exit("set", "missing", "json", json);
    1336         if (selector == undefined) return _json.exit("set", "missing", "selector", selector);
    1337         if (!_w.isString(selector)) return _json.exit("set", "noString", "selector", selector);
    1338         return value ? deepJSON(json, selector, value) : _json.remove(json, selector);
    1339         // return deepJSON(json, selector, value); // Now removes the property if the value is empty. Maybe should keep it instead?
    1340 
    1341     };
    1342 
    1343     _json.remove = function (json, selector) {
    1344 
    1345         if (json == undefined) return _json.exit("remove", "missing", "json", json);
    1346         if (selector == undefined) return _json.exit("remove", "missing", "selector", selector);
    1347         if (!_w.isString(selector)) return _json.exit("remove", "noString", "selector", selector);
    1348         return deepJSON(json, selector, null, true);
    1349 
    1350     }
    1351 
    1352     _json.push = function (json, selector, value, force) {
    1353 
    1354         if (json == undefined) return _json.exit("push", "missing", "json", json);
    1355         if (selector == undefined) return _json.exit("push", "missing", "selector", selector);
    1356         var array = _json.get(json, selector);
    1357         if (!_w.isArray(array)) {
    1358             if (force) {
    1359                 array = [];
    1360             } else {
    1361                 return _json.exit("push", "noArray", "array", array);
    1362             }
    1363         }
    1364         array.push(value);
    1365         return _json.set(json, selector, array);
    1366 
    1367     }
    1368 
    1369     _json.unshift = function (json, selector, value) {
    1370 
    1371         if (json == undefined) return _json.exit("unshift", "missing", "json", json);
    1372         if (selector == undefined) return _json.exit("unshift", "missing", "selector", selector);
    1373         if (value == undefined) return _json.exit("unshift", "missing", "value", value);
    1374         var array = _json.get(json, selector);
    1375         if (!_w.isArray(array)) return _json.exit("unshift", "noArray", "array", array);
    1376         array.unshift(value);
    1377         return _json.set(json, selector, array);
    1378 
    1379     }
    1380 
    1381     _json.flatten = function (json) {
    1382 
    1383         if (json.constructor.name != "Object") return _json.exit("flatten", "noJSON", "json", json);
    1384 
    1385         var result = {};
    1386         function recurse(cur, prop) {
    1387             if (Object(cur) !== cur) {
    1388                 result[prop] = cur;
    1389             } else if (Array.isArray(cur)) {
    1390                 for (var i = 0, l = cur.length; i < l; i++) {
    1391                     recurse(cur[i], prop ? prop + "." + i : "" + i);
    1392                     if (l == 0) result[prop] = [];
    1393                 }
    1394             } else {
    1395                 var isEmpty = true;
    1396                 for (var p in cur) {
    1397                     isEmpty = false;
    1398                     recurse(cur[p], prop ? prop + "." + p : p);
    1399                 }
    1400                 if (isEmpty) result[prop] = {};
    1401             }
    1402         }
    1403         recurse(json, "");
    1404         return result;
    1405 
    1406     }
    1407 
    1408     _json.unflatten = function (data) {
    1409 
    1410         if (Object(data) !== data || Array.isArray(data))
    1411             return data;
    1412         var result = {}, cur, prop, idx, last, temp;
    1413         for (var p in data) {
    1414             cur = result, prop = "", last = 0;
    1415             do {
    1416                 idx = p.indexOf(".", last);
    1417                 temp = p.substring(last, idx !== -1 ? idx : undefined);
    1418                 cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : {}));
    1419                 prop = temp;
    1420                 last = idx + 1;
    1421             } while (idx >= 0);
    1422             cur[prop] = data[p];
    1423         }
    1424         return result[""];
    1425 
    1426     }
    1427 
    1428     _json.prettyprint = function (json) {
    1429 
    1430         return JSON.stringify(json, undefined, 2);
    1431 
    1432     }
    1433 
    1434     //////////////////////////////////////////
    1435     // wQuery (mini replacement for jQuery) //
    1436     //////////////////////////////////////////
    1437 
    1438     var wQuery = function () { };
    1439     wQuery.constructor = wQuery;
    1440 
    1441     wQuery.prototype.dom = function (selector, createOptions) {
    1442 
    1443         var self = this,
    1444                 elements = [];
    1445 
    1446         if (createOptions) {
    1447             var element = document.createElement(selector);
    1448             for (var k in createOptions) {
    1449                 element[k] = createOptions[k];
    1450             }
    1451         } else {
    1452             if (_w.isString(selector)) {
    1453                 elements = [].slice.call(document.querySelectorAll(selector));
    1454             } else {
    1455                 if (_w.isObject(selector) && selector.attributes) { elements = [selector]; }
    1456             }
    1457             self._elements = elements;
    1458             self.length = elements.length;
    1459             return self;
    1460         }
    1461 
    1462     }
    1463 
    1464     wQuery.prototype.on = function (events, fn) {
    1465 
    1466         var self = this,
    1467                 elements = self._elements;
    1468         events = events.split(" ");
    1469         for (var i = 0, lenEl = elements.length; i < lenEl; i++) {
    1470             var element = elements[i];
    1471             for (var j = 0, lenEv = events.length; j < lenEv; j++) {
    1472                 if (element.addEventListener) { element.addEventListener(events[j], fn, false); }
    1473             }
    1474         }
    1475 
    1476     }
    1477 
    1478     wQuery.prototype.find = function (selector) {
    1479 
    1480         var self = this,
    1481                 element = self.get(0),
    1482                 elements = [];
    1483 
    1484         if (_w.isString(selector)) {
    1485             elements = [].slice.call(element.querySelectorAll(selector));
    1486         }
    1487         self._elements = elements;
    1488         return self;
    1489 
    1490     }
    1491 
    1492     wQuery.prototype.get = function (index, chain) {
    1493 
    1494         var self = this,
    1495                 elements = self._elements || [],
    1496                 element = elements[index] || {};
    1497 
    1498         if (chain) {
    1499             self._element = element;
    1500             return self;
    1501         } else {
    1502             return _w.isNumber(index) ? element : elements;
    1503         }
    1504 
    1505     }
    1506 
    1507     wQuery.prototype.reverse = function () {
    1508         this._elements = this._elements.reverse();
    1509         return this;
    1510     }
    1511 
    1512     wQuery.prototype.val = function (value) {
    1513         return this.prop("value", value);
    1514     }
    1515 
    1516     wQuery.prototype.type = function (value) {
    1517         return this.prop("type", value);
    1518     }
    1519 
    1520     wQuery.prototype.html = function (value) {
    1521         return this.prop("innerHTML", value);
    1522     }
    1523 
    1524     wQuery.prototype.text = function (value) {
    1525         return this.prop("innerHTML", escapeHTML(value));
    1526     }
    1527 
    1528     wQuery.prototype.prop = function (prop, value) {
    1529 
    1530         var self = this,
    1531                 elements = self._elements;
    1532 
    1533         for (var i in elements) {
    1534             if (_w.isUndefined(value)) {
    1535                 return elements[i][prop];
    1536             } else {
    1537                 elements[i][prop] = value;
    1538             }
    1539         }
    1540 
    1541     }
    1542 
    1543     wQuery.prototype.attr = function (attr, value) {
    1544 
    1545         var self = this,
    1546                 elements = self._elements;
    1547         for (var i in elements) {
    1548             if (value === undefined) {
    1549                 return elements[i].getAttribute(attr);
    1550             } else {
    1551                 elements[i].setAttribute(attr, value);
    1552             }
    1553         }
    1554         return self;
    1555 
    1556     }
    1557 
    1558     wQuery.prototype.removeAttr = function (attr) {
    1559         var self = this;
    1560         for (var i in self._elements) self._elements[i].removeAttribute(attr);
    1561         return self;
    1562     }
    1563 
    1564     wQuery.prototype.addClass = function (c) {
    1565         var self = this;
    1566         for (var i in self._elements) self._elements[i].classList.add(c);
    1567         return self;
    1568     }
    1569 
    1570     wQuery.prototype.removeClass = function (c) {
    1571         var self = this;
    1572         for (var i in self._elements) self._elements[i].classList.remove(c);
    1573         return self;
    1574     }
    1575 
    1576     wQuery.prototype.parents = function (selector) {
    1577         var self = this,
    1578                 element = self.get(0),
    1579                 parent = element.parentNode,
    1580                 parents = [];
    1581 
    1582         while (parent !== null) {
    1583             var o = parent,
    1584                     matches = matchesSelector(o, selector),
    1585                     isNotDomRoot = (o.doctype === undefined) ? true : false;
    1586             if (!selector) { matches = true; }
    1587             if (matches && isNotDomRoot) { parents.push(o); }
    1588             parent = o.parentNode;
    1589         }
    1590         self._elements = parents;
    1591         return self;
    1592     }
    1593 
    1594     wQuery.prototype.parent = function (selector) {
    1595         var self = this,
    1596                 element = self.get(0),
    1597                 o = element.parentNode,
    1598                 matches = matchesSelector(o, selector);
    1599         if (!selector) { matches = true; }
    1600         return matches ? o : {};
    1601     }
    1602 
    1603     wQuery.prototype.clone = function (chain) {
    1604         var self = this,
    1605                 element = self.get(0),
    1606                 clone = element.cloneNode(true);
    1607         self._elements = [clone];
    1608         return chain ? self : clone;
    1609     }
    1610 
    1611     wQuery.prototype.empty = function (chain) {
    1612         var self = this,
    1613                 element = self.get(0);
    1614         if (!element || !element.hasChildNodes) { return chain ? self : element; }
    1615 
    1616         while (element.hasChildNodes()) {
    1617             element.removeChild(element.lastChild);
    1618         }
    1619         return chain ? self : element;
    1620     }
    1621 
    1622     wQuery.prototype.replaceWith = function (newDOM) {
    1623         var self = this,
    1624                 oldDOM = self.get(0),
    1625                 parent = oldDOM.parentNode;
    1626         parent.replaceChild(newDOM, oldDOM);
    1627     }
    1628 
    1629     wQuery.prototype.ready = function (callback) {
    1630 
    1631         if (document && _w.isFunction(document.addEventListener)) {
    1632             document.addEventListener("DOMContentLoaded", callback, false);
    1633         } else if (window && _w.isFunction(window.addEventListener)) {
    1634             window.addEventListener("load", callback, false);
    1635         } else {
    1636             document.onreadystatechange = function () {
    1637                 if (document.readyState === "complete") { callback(); }
    1638             }
    1639         }
    1640 
    1641     }
    1642 
    1643     //////////////////////
    1644     // WATCH DOM EVENTS //
    1645     //////////////////////
    1646 
    1647     way = new WAY();
    1648 
    1649     var timeoutInput = null;
    1650     var eventInputChange = function (e) {
    1651         if (timeoutInput) { clearTimeout(timeoutInput); }
    1652         timeoutInput = setTimeout(function () {
    1653             var element = w.dom(e.target).get(0);
    1654             way.dom(element).toStorage();
    1655         }, way.options.timeout);
    1656     }
    1657 
    1658     var eventClear = function (e) {
    1659         e.preventDefault();
    1660         var options = way.dom(this).getOptions();
    1661         way.remove(options.data, options);
    1662     }
    1663 
    1664     var eventPush = function (e) {
    1665         e.preventDefault();
    1666         var options = way.dom(this).getOptions();
    1667         if (!options || !options["action-push"]) { return false; }
    1668         var split = options["action-push"].split(":"),
    1669                 selector = split[0] || null,
    1670                 value = split[1] || null;
    1671         way.push(selector, value, options);
    1672     }
    1673 
    1674     var eventRemove = function (e) {
    1675         e.preventDefault();
    1676         var options = way.dom(this).getOptions();
    1677         if (!options || !options["action-remove"]) { return false; }
    1678         way.remove(options["action-remove"], options);
    1679     }
    1680 
    1681     var timeoutDOM = null;
    1682     var eventDOMChange = function () {
    1683 
    1684         // We need to register dynamically added bindings so we do it by watching DOM changes
    1685         // We use a timeout since "DOMSubtreeModified" gets triggered on every change in the DOM (even input value changes)
    1686         // so we can limit the number of scans when a user is typing something
    1687         if (timeoutDOM) { clearTimeout(timeoutDOM); }
    1688         timeoutDOM = setTimeout(function () {
    1689             way.registerDependencies();
    1690             setEventListeners();
    1691         }, way.options.timeoutDOM);
    1692 
    1693     }
    1694 
    1695     //////////////
    1696     // INITIATE //
    1697     //////////////
    1698 
    1699     w = new wQuery();
    1700     way.w = w;
    1701 
    1702     var setEventListeners = function () {
    1703 
    1704         w.dom("body").on("DOMSubtreeModified", eventDOMChange);
    1705         w.dom("[" + tagPrefix + "-data]").on("input change", eventInputChange);
    1706         w.dom("[" + tagPrefix + "-clear]").on("click", eventClear);
    1707         w.dom("[" + tagPrefix + "-action-remove]").on("click", eventRemove);
    1708         w.dom("[" + tagPrefix + "-action-push]").on("click", eventPush);
    1709 
    1710     }
    1711 
    1712     var eventInit = function () {
    1713 
    1714         setEventListeners();
    1715         way.restore();
    1716         way.setDefaults();
    1717         way.registerDependencies();
    1718         way.updateDependencies();
    1719 
    1720     }
    1721 
    1722     w.ready(eventInit);
    1723 
    1724     return way;
    1725 
    1726 }));
    View Code

    修复,绑定null值。

    deepJSON方法增加null判断。null 转‘’。

  • 相关阅读:
    广播系统android安全:flag FLAG_RECEIVER_REGISTERED_ONLY的意义
    产品类大话设计模式——简单工厂模式
    打印数组算法:堆栈与深度优先搜索(迷宫问题)
    函数声明第四章利用函数实现指定的功能
    构造函数调用C++ 类和动态内存分配
    命令密码MySQL忘记密码恢复密码的实现方法
    线程资源PHP源码分析之线程安全模型
    编译类【COCOS2DXLUA 脚本开发之十四】解决自定义CPP类通过TOLUA++ BINDING LUACOCOS2D后编译到ANDROID运行黑屏(没有调用自定义CPP类)的问题!
    语句数据库ubuntu下mysql的常用命令
    eclipse中配置tomcat
  • 原文地址:https://www.cnblogs.com/blogs2014/p/7424175.html
Copyright © 2011-2022 走看看