-
Notifications
You must be signed in to change notification settings - Fork 11
/
jquery.gdb.min.js
executable file
·1 lines (1 loc) · 18 KB
/
jquery.gdb.min.js
1
(function(){var gdbFactory=function(jQuery){var $=jQuery||window.jQuery;var instanceID=0;var GDB=function(modelsToMonitorObject,userOptionsObject){var GDB=this||{};GDB.id=instanceID;instanceID++;var listenForEvents;var observeObjects;var witnessedObjects={};GDB.witnessedObjects=witnessedObjects;GDB.helpers={isEventSupported:function(eventName){var tags={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};var el=document.createElement(tags[eventName]||"div");eventName="on"+eventName;var isSupported=(eventName in el);if(!isSupported){el.setAttribute(eventName,"return;");isSupported=typeof el[eventName]=="function"}el=null;return isSupported},escapeForRegEx:function(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isElement:function(o){return(typeof HTMLElement==="object"?o instanceof HTMLElement:o&&typeof o==="object"&&o!==null&&o.nodeType===1&&typeof o.nodeName==="string")}};var options={rootElementSelectorString:null,rootElement:"body",templateOpeningDelimiter:"<<",templateClosingDelimiter:">>",realtime:true,renderOnInitialization:true,dataBindToAttr:"data-bindto",dataWatchingAttr:"data-watching",dataTemplateAttr:"data-gdb-template",dataParseWithAttr:"data-parsewith",dataBindOnAttrPrefix:"data-bindon-",listenForEvents:"click dblclick change input keydown mouseover mouseout keypress keyup mousedown mouseup focus blur",bindAsTextOnly:false,insertPolyfills:true,debugLogging:false,modelChangeCallback:null,elementChangeCallback:null};if(userOptionsObject!==undefined){options=$.extend(options,userOptionsObject)}if(options.insertPolyfills===true){loadPolyFills()}if(options.rootElementSelectorString===null){(function(){var el_get=function(el){if(typeof el==="string"){el=(document).querySelector(el)}else{if(isjQuery_el(el)){el=el.get(0)}}return el};var isjQuery_el=function($el){return $el instanceof jQuery};options.rootElementSelectorString=el_get(options.rootElement)}())}var modelsToMonitor=modelsToMonitorObject;GDB.getBoundElementFromModelPath=function(path){return $("["+options.dataBindToAttr+'="'+path+'"]')};GDB.getModelPathFromBoundElement=function(selector){var $element=$(selector);if($element.is("["+options.dataBindToAttr+"]")){return $element.attr(options.dataBindToAttr)}else{return false}};GDB.getValueFromModelPath=function(path){var modelValue=$.extend(true,{},modelsToMonitor);if(typeof path==="undefined"){return}path=path.replace(/\[(\w+)\]/g,".$1");path=path.replace(/^\./,"");var array=path.split(".");while(array.length){var item=array.shift();if(item in modelValue){modelValue=modelValue[item]}else{return}}return modelValue};GDB.getModelPathFromModelPart=function(modelPartObject){var modelPath=null;$.each(witnessedObjects,function(modelPathLocation,value){if(value===modelPartObject){modelPath=modelPathLocation;return false}});return modelPath};GDB.getBoundElementsForModelPart=function(modelPartObject){var path=GDB.getModelPathFromModelPart(modelPartObject);return $("["+options.dataBindToAttr+'^="'+path+'"]').filter(function(){var attrValue=$(this).attr(options.dataBindToAttr);var parent=attrValue.replace(/\[(\w+)\]/g,".$1");var parentLocationArr=parent.split(".");parentLocationArr.pop();parent=parentLocationArr.join(".");if(parent==path){return true}})};GDB.render=function(){var $selector=$(options.rootElementSelectorString).find("["+options.dataBindToAttr+"],["+options.dataWatchingAttr+"]");$selector.each(function(){setElementsToValue($(this),GDB.getValueFromModelPath($(this).attr(options.dataBindToAttr)))})};GDB.destroyInstance=function(){$(options.rootElementSelectorString).off(setEventsToNamespaced(listenForEvents+" "+options.listenForEvents));observeObjects(true,modelsToMonitor)};var setEventsToNamespaced=function(eventsString){var splitEvents=eventsString.split(" ");splitEvents.forEach(function(item,i){splitEvents[i]=item+".gdbInstance"+GDB.id});return splitEvents.join(" ")};var setElementsToValue=function($element,value){$element.each(function(){if($(this).is("["+options.dataWatchingAttr+"]["+options.dataParseWithAttr+"]")){var newValue=eval("modelsToMonitor."+$(this).attr(options.dataParseWithAttr)+".in()")}else{if($(this).is("["+options.dataWatchingAttr+"]["+options.dataTemplateAttr+"]")){var modelLocations=$(this).attr(options.dataWatchingAttr).split(",");var template=$(this).attr(options.dataTemplateAttr);modelLocations.forEach(function(location,index){template=template.replace(new RegExp(GDB.helpers.escapeForRegEx(options.templateOpeningDelimiter)+(index+1)+GDB.helpers.escapeForRegEx(options.templateClosingDelimiter),"g"),GDB.getValueFromModelPath(location.trim()))});newValue=template}else{newValue=value}}if($(this).is("input,select,textarea")){if($(this).is(":checkbox")){$element.each(function(){var $this=$(this);var isValue=false;newValue.forEach(function(newValue){if($this.val()==newValue){if(!$this.is(":checked")){$this.prop("checked",true)}isValue=true}});if(!isValue){$this.prop("checked",false)}})}else{if($(this).is(":radio")){$element.each(function(){var $this=$(this);if($this.attr("value")==newValue){$this.prop("checked",true)}else{$this.prop("checked",false)}})}else{if(!$(this).is("["+options.dataWatchingAttr+"]["+options.dataTemplateAttr+"]:focus")){if($(this).val()!=newValue){$(this).val(newValue)}}}}}else{if(!options.bindAsTextOnly){if(!$(this).is("["+options.dataWatchingAttr+"]["+options.dataTemplateAttr+"]:focus")){if($(this).html()!=newValue){$(this).html(newValue)}}}else{if(!$(this).is("["+options.dataWatchingAttr+"]["+options.dataTemplateAttr+"]:focus")){if($(this).text()!=newValue){$(this).text(newValue)}}}}})};if(options.renderOnInitialization){GDB.render()}listenForEvents=(options.realtime?(GDB.helpers.isEventSupported("input")?"input":"keyup")+" paste ":"")+" change blur";$(options.rootElementSelectorString).on(setEventsToNamespaced(listenForEvents),"["+options.dataBindToAttr+"],["+options.dataTemplateAttr+"],["+options.dataParseWithAttr+"]",function(e){var $this=$(this);var value="";if($this.is("["+options.dataBindToAttr+"],["+options.dataParseWithAttr+"],["+options.dataTemplateAttr+"]")){var modelLocation=$this.attr(options.dataBindToAttr);if($this.is("input,select,textarea")){if($this.is(":checkbox")){value=$("["+options.dataBindToAttr+"='"+$this.attr(options.dataBindToAttr)+"'][name='"+$this.attr("name")+"']:checked").map(function(){return $(this).val()}).toArray()}else{value=$this.val()}}else{value=options.bindAsTextOnly?$this.text():$this.html()}var rawValue=value;if(!$.isArray(value)){value="'"+value.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(/\n/g,"\\n")+"'"}else{value=JSON.stringify(value)}if(modelLocation){eval("modelsToMonitor."+modelLocation+"="+value)}if($this.is("["+options.dataParseWithAttr+"]")){eval("modelsToMonitor."+$this.attr(options.dataParseWithAttr)+".out("+value+")")}if($this.is("["+options.dataWatchingAttr+"]["+options.dataTemplateAttr+"]")){var splitBys=$(this).attr(options.dataTemplateAttr).split(new RegExp(GDB.helpers.escapeForRegEx(options.templateOpeningDelimiter)+"\\d+"+GDB.helpers.escapeForRegEx(options.templateClosingDelimiter),"g"));var toDelete=[];splitBys.forEach(function(item,i){if(item.length===0){toDelete.push(i)}else{splitBys[i]=GDB.helpers.escapeForRegEx(item)}});var subtract=0;toDelete.forEach(function(index){splitBys.splice(parseInt(index)-subtract,1);subtract++});var modelLocationValuesArray=[];var valueArray=rawValue.split(new RegExp(splitBys.join("|"),"g"));var indexArray=$(this).attr(options.dataTemplateAttr).split(new RegExp(splitBys.join("|"),"g"));var modelLocations=$(this).attr(options.dataWatchingAttr).split(",");indexArray.forEach(function(value,i){var index=(parseInt(value.replace(new RegExp(GDB.helpers.escapeForRegEx(options.templateOpeningDelimiter)+"|"+GDB.helpers.escapeForRegEx(options.templateClosingDelimiter),"g"),""))-1);modelLocationValuesArray.push({location:modelLocations[index].trim(),value:typeof valueArray[i]==="undefined"?'""':JSON.stringify(valueArray[i])})});modelLocationValuesArray.forEach(function(modelLocationAndValue){console.log("modelsToMonitor."+modelLocationAndValue.location+"="+modelLocationAndValue.value);eval("modelsToMonitor."+modelLocationAndValue.location+"="+modelLocationAndValue.value)})}if(options.debugLogging){console.log(modelLocation+" is now equal to "+value+' as per changes made in the view as witnessed by the "'+e.type+'" event.')}if(typeof options.elementChangeCallback==="function"){if(options.debugLogging){console.log("Bound element change callback executed for change in "+modelLocation)}options.elementChangeCallback({locationPathString:modelLocation,$boundElement:$this,newValue:rawValue})}else{if(options.debugLogging){console.log("No callback supplied for bound element change thus no function was called")}}}});var elementsToWatch=(function(){var listOfAttributes="";options.listenForEvents.split(" ").forEach(function(eventName){listOfAttributes+="["+options.dataBindOnAttrPrefix+eventName+"],"});return listOfAttributes.substring(0,listOfAttributes.length-1)}());$(options.rootElementSelectorString).on(setEventsToNamespaced(options.listenForEvents),elementsToWatch,function(e){var functionLocation=$(this).attr(options.dataBindOnAttrPrefix+e.type);if(functionLocation){e.gdbBoundData=$(this).attr(options.dataBindToAttr)?eval("modelsToMonitor."+$(this).attr(options.dataBindToAttr)):null;e.gdbWatchingData=(function(){if($(this).attr(options.dataWatchingAttr)){var watchingArr=[];$(this).attr(options.dataWatchingAttr).split(",").forEach(function(modelLocation){watchingArr.push(eval("modelsToMonitor."+modelLocation.trim()))});return watchingArr}else{return null}}());e.gdbParent=(function(){var parent=functionLocation.replace(/\[(\w+)\]/g,".$1");var parentLocationArr=parent.split(".");parentLocationArr.pop();parent=parentLocationArr.join(".");return GDB.getValueFromModelPath(parent)}());eval("modelsToMonitor."+functionLocation+".apply(this, [e]);")}});var observerFunctions={};observeObjects=function(unobserve,objectToObserve,objectLocationString,previousObjects){var observationAction=unobserve?"unobserve":"observe";previousObjects=previousObjects||[];$.each(objectToObserve,function(key,value){if((value!==null&&(typeof value==="object"||value instanceof Array))&&!GDB.helpers.isElement(value)&&function(){var wasNotSeen=true;previousObjects.forEach(function(object){if(object===value){wasNotSeen=false}});return wasNotSeen}()){previousObjects.push(value);var thisLocation="";if(typeof objectLocationString==="undefined"){thisLocation=""+key}else{if(!isNaN(key)){thisLocation=objectLocationString+"["+key+"]"}else{thisLocation=objectLocationString+"."+key}}witnessedObjects[thisLocation]=value;var changeHandlerFunction=observerFunctions[thisLocation+key]?observerFunctions[thisLocation+key]:function(changes){changes.forEach(function(change){var key=!isNaN(change.name)?"["+change.name+"]":"."+change.name;var modelPath=thisLocation+key;var elementSelector="["+options.dataBindToAttr+"='"+modelPath+"'],["+options.dataWatchingAttr+"*='"+modelPath+",'],["+options.dataWatchingAttr+"$='"+modelPath+"']";var newValue=change.object[change.name];var oldValue=change.oldValue;var $element=$(elementSelector);if(typeof newValue==="object"||newValue instanceof Array){observeObjects(unobserve,value,thisLocation,previousObjects)}setElementsToValue($element,newValue);if($.isArray(newValue)){var logValue=JSON.stringify(newValue)}else{logValue="'"+newValue+"'"}if(options.debugLogging){console.log(thisLocation+key+" is now equal to "+logValue+" as observed in the model.")}if(typeof options.modelChangeCallback==="function"){if(options.debugLogging){console.log("Model change callback executed for change in "+thisLocation+key)}options.modelChangeCallback({locationPathString:modelPath,$boundElements:$element,newValue:newValue,oldValue:oldValue})}else{if(options.debugLogging){console.log("No callback supplied for model change thus no function was called")}}})};if(!observerFunctions[thisLocation+key]){observerFunctions[thisLocation+key]=changeHandlerFunction}Object[observationAction](value,changeHandlerFunction);observeObjects(unobserve,value,thisLocation,previousObjects)}})};observeObjects(false,modelsToMonitor)};var loadPolyFills=function(){if(!Object.observe){(function(extend,global){var isCallable=(function(toString){var s=toString.call(toString),u=typeof u;return typeof global.alert==="object"?function(f){return s===toString.call(f)||(!!f&&typeof f.toString==u&&typeof f.valueOf==u&&/^\s*\bfunction\b/.test(""+f))}:function(f){return s===toString.call(f)}})(extend.prototype.toString);function isNode(o){return(typeof Node==="object"?o instanceof Node:o&&typeof o==="object"&&typeof o.nodeType==="number"&&typeof o.nodeName==="string")}function isElement(o){return(typeof HTMLElement==="object"?o instanceof HTMLElement:o&&typeof o==="object"&&o!==null&&o.nodeType===1&&typeof o.nodeName==="string")}var _isImmediateSupported=(function(){return !!global.setImmediate})();var _doCheckCallback=(function(){if(_isImmediateSupported){return function(f){return setImmediate(f)}}else{return function(f){return setTimeout(f,10)}}})();var _clearCheckCallback=(function(){if(_isImmediateSupported){return function(id){clearImmediate(id)}}else{return function(id){clearTimeout(id)}}})();var isNumeric=function(n){return !isNaN(parseFloat(n))&&isFinite(n)};var sameValue=function(x,y){if(x===y){return x!==0||1/x===1/y}return x!==x&&y!==y};var isAccessorDescriptor=function(desc){if(typeof(desc)==="undefined"){return false}return("get" in desc||"set" in desc)};var isDataDescriptor=function(desc){if(typeof(desc)==="undefined"){return false}return("value" in desc||"writable" in desc)};var validateArguments=function(O,callback){if(typeof(O)!=="object"){throw new TypeError("Object.observeObject called on non-object")}if(isCallable(callback)===false){throw new TypeError("Object.observeObject: Expecting function")}if(Object.isFrozen(callback)===true){throw new TypeError("Object.observeObject: Expecting unfrozen function")}};var Observer=(function(){var wraped=[];var Observer=function(O,callback){validateArguments(O,callback);Object.getNotifier(O).addListener(callback);if(wraped.indexOf(O)===-1){wraped.push(O)}else{Object.getNotifier(O)._checkPropertyListing()}};Observer.prototype.deliverChangeRecords=function(O){Object.getNotifier(O).deliverChangeRecords()};wraped.lastScanned=0;var f=(function(wrapped){return function(){var i=0,l=wrapped.length,startTime=new Date(),takingTooLong=false;for(i=wrapped.lastScanned;(i<l)&&(!takingTooLong);i++){Object.getNotifier(wrapped[i])._checkPropertyListing();takingTooLong=((new Date())-startTime)>100}wrapped.lastScanned=i<l?i:0;_doCheckCallback(f)}})(wraped);_doCheckCallback(f);return Observer})();var Notifier=function(watching){var _listeners=[],_updates=[],_updater=false,properties=[],values=[];var self=this;Object.defineProperty(self,"_watching",{enumerable:true,get:(function(watched){return function(){return watched}})(watching)});var wrapProperty=function(object,prop){var propType=typeof(object[prop]),descriptor=Object.getOwnPropertyDescriptor(object,prop);if((prop==="getNotifier")||isAccessorDescriptor(descriptor)||(!descriptor.enumerable)){return false}if((object instanceof Array)&&isNumeric(prop)){var idx=properties.length;properties[idx]=prop;values[idx]=object[prop];return true}(function(idx,prop){properties[idx]=prop;values[idx]=object[prop];Object.defineProperty(object,prop,{get:function(){return values[idx]},set:function(value){if(!sameValue(values[idx],value)){Object.getNotifier(object).queueUpdate(object,prop,"updated",values[idx]);values[idx]=value}}})})(properties.length,prop);return true};self._checkPropertyListing=function(dontQueueUpdates){var object=self._watching,keys=Object.keys(object),i=0,l=keys.length;var newKeys=[],oldKeys=properties.slice(0),updates=[];var prop,queueUpdates=!dontQueueUpdates,propType,value,idx,aLength;if(object instanceof Array){aLength=properties.length}for(i=0;i<l;i++){prop=keys[i];value=object[prop];propType=typeof(value);if((idx=properties.indexOf(prop))===-1){if(wrapProperty(object,prop)&&queueUpdates){self.queueUpdate(object,prop,"new",null,object[prop])}}else{if((object instanceof Array)&&(isNumeric(prop))){if(values[idx]!==value){if(queueUpdates){self.queueUpdate(object,prop,"updated",values[idx],value)}values[idx]=value}}oldKeys.splice(oldKeys.indexOf(prop),1)}}if(object instanceof Array&&object.length!==aLength){if(queueUpdates){self.queueUpdate(object,"length","updated",aLength,object)}}if(queueUpdates){l=oldKeys.length;for(i=0;i<l;i++){idx=properties.indexOf(oldKeys[i]);self.queueUpdate(object,oldKeys[i],"deleted",values[idx]);properties.splice(idx,1);values.splice(idx,1)}}};self.addListener=function(callback){var idx=_listeners.indexOf(callback);if(idx===-1){_listeners.push(callback)}};self.removeListener=function(callback){var idx=_listeners.indexOf(callback);if(idx>-1){_listeners.splice(idx,1)}};self.listeners=function(){return _listeners};self.queueUpdate=function(what,prop,type,was){this.queueUpdates([{type:type,object:what,name:prop,oldValue:was}])};self.queueUpdates=function(updates){var self=this,i=0,l=updates.length||0,update;for(i=0;i<l;i++){update=updates[i];_updates.push(update)}if(_updater){_clearCheckCallback(_updater)}_updater=_doCheckCallback(function(){_updater=false;self.deliverChangeRecords()})};self.deliverChangeRecords=function(){var i=0,l=_listeners.length,retval;for(i=0;i<l;i++){if(_listeners[i]){if(_listeners[i]===console.log){console.log(_updates)}else{_listeners[i](_updates)}}}_updates=[]};self._checkPropertyListing(true)};var _notifiers=[],_indexes=[];extend.getNotifier=function(O){var idx=_indexes.indexOf(O),notifier=idx>-1?_notifiers[idx]:false;if(!notifier){idx=_indexes.length;_indexes[idx]=O;notifier=_notifiers[idx]=new Notifier(O)}return notifier};extend.observe=function(O,callback){if(!isElement(O)){return new Observer(O,callback)}};extend.unobserve=function(O,callback){validateArguments(O,callback);var idx=_indexes.indexOf(O),notifier=idx>-1?_notifiers[idx]:false;if(!notifier){return}notifier.removeListener(callback);if(notifier.listeners().length===0){_indexes.splice(idx,1);_notifiers.splice(idx,1)}}})(Object,this)}};return GDB};if(typeof define==="function"&&define.amd){define("gdb",["jquery"],function(jquery){return gdbFactory(jquery)})}else{if(typeof exports==="object"){module.exports=gdbFactory(require("jquery"))}else{jQuery(function(){window.GDB=gdbFactory(window.jQuery)})}}}());