| Index: pkg/polymer/lib/src/js/polymer/polymer.js
|
| diff --git a/pkg/polymer/lib/src/js/polymer/polymer.js b/pkg/polymer/lib/src/js/polymer/polymer.js
|
| index 6a8caf22aaa867d74747e12d18a4e0c8c65aa86d..b8b62b0a6dcf83d08e443c9f857d048172eff9d1 100644
|
| --- a/pkg/polymer/lib/src/js/polymer/polymer.js
|
| +++ b/pkg/polymer/lib/src/js/polymer/polymer.js
|
| @@ -7,9 +7,11823 @@
|
| * Code distributed by Google as part of the polymer project is also
|
| * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| */
|
| -// @version: 0.4.2-8c339cf
|
| -window.PolymerGestures={},function(a){var b=!1,c=document.createElement("meta");if(c.createShadowRoot){var d=c.createShadowRoot(),e=document.createElement("span");d.appendChild(e),c.addEventListener("testpath",function(a){a.path&&(b=a.path[0]===e),a.stopPropagation()});var f=new CustomEvent("testpath",{bubbles:!0});document.head.appendChild(c),e.dispatchEvent(f),c.parentNode.removeChild(c),d=e=null}c=null;var g={shadow:function(a){return a?a.shadowRoot||a.webkitShadowRoot:void 0},canTarget:function(a){return a&&Boolean(a.elementFromPoint)},targetingShadow:function(a){var b=this.shadow(a);return this.canTarget(b)?b:void 0},olderShadow:function(a){var b=a.olderShadowRoot;if(!b){var c=a.querySelector("shadow");c&&(b=c.olderShadowRoot)}return b},allShadows:function(a){for(var b=[],c=this.shadow(a);c;)b.push(c),c=this.olderShadow(c);return b},searchRoot:function(a,b,c){var d,e;return a?(d=a.elementFromPoint(b,c),d?e=this.targetingShadow(d):a!==document&&(e=this.olderShadow(a)),this.searchRoot(e,b,c)||d):void 0},owner:function(a){if(!a)return document;for(var b=a;b.parentNode;)b=b.parentNode;return b.nodeType!=Node.DOCUMENT_NODE&&b.nodeType!=Node.DOCUMENT_FRAGMENT_NODE&&(b=document),b},findTarget:function(a){if(b&&a.path&&a.path.length)return a.path[0];var c=a.clientX,d=a.clientY,e=this.owner(a.target);return e.elementFromPoint(c,d)||(e=document),this.searchRoot(e,c,d)},findTouchAction:function(a){var c;if(b&&a.path&&a.path.length){for(var d=a.path,e=0;e<d.length;e++)if(c=d[e],c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action")}else for(c=a.target;c;){if(c.nodeType===Node.ELEMENT_NODE&&c.hasAttribute("touch-action"))return c.getAttribute("touch-action");c=c.parentNode||c.host}return"auto"},LCA:function(a,b){if(a===b)return a;if(a&&!b)return a;if(b&&!a)return b;if(!b&&!a)return document;if(a.contains&&a.contains(b))return a;if(b.contains&&b.contains(a))return b;var c=this.depth(a),d=this.depth(b),e=c-d;for(e>=0?a=this.walk(a,e):b=this.walk(b,-e);a&&b&&a!==b;)a=a.parentNode||a.host,b=b.parentNode||b.host;return a},walk:function(a,b){for(var c=0;a&&b>c;c++)a=a.parentNode||a.host;return a},depth:function(a){for(var b=0;a;)b++,a=a.parentNode||a.host;return b},deepContains:function(a,b){var c=this.LCA(a,b);return c===a},insideNode:function(a,b,c){var d=a.getBoundingClientRect();return d.left<=b&&b<=d.right&&d.top<=c&&c<=d.bottom},path:function(a){var c;if(b&&a.path&&a.path.length)c=a.path;else{c=[];for(var d=this.findTarget(a);d;)c.push(d),d=d.parentNode||d.host}return c}};a.targetFinding=g,a.findTarget=g.findTarget.bind(g),a.deepContains=g.deepContains.bind(g),a.insideNode=g.insideNode}(window.PolymerGestures),function(){function a(a){return"html /deep/ "+b(a)}function b(a){return'[touch-action="'+a+'"]'}function c(a){return"{ -ms-touch-action: "+a+"; touch-action: "+a+";}"}var d=["none","auto","pan-x","pan-y",{rule:"pan-x pan-y",selectors:["pan-x pan-y","pan-y pan-x"]},"manipulation"],e="",f="string"==typeof document.head.style.touchAction,g=!window.ShadowDOMPolyfill&&document.head.createShadowRoot;if(f){d.forEach(function(d){String(d)===d?(e+=b(d)+c(d)+"\n",g&&(e+=a(d)+c(d)+"\n")):(e+=d.selectors.map(b)+c(d.rule)+"\n",g&&(e+=d.selectors.map(a)+c(d.rule)+"\n"))});var h=document.createElement("style");h.textContent=e,document.head.appendChild(h)}}(),function(a){var b=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","pageX","pageY"],c=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0],d=function(){return function(){}},e={preventTap:d,makeBaseEvent:function(a,b){var c=document.createEvent("Event");return c.initEvent(a,b.bubbles||!1,b.cancelable||!1),c.preventTap=e.preventTap(c),c},makeGestureEvent:function(a,b){b=b||Object.create(null);for(var c,d=this.makeBaseEvent(a,b),e=0,f=Object.keys(b);e<f.length;e++)c=f[e],d[c]=b[c];return d},makePointerEvent:function(a,d){d=d||Object.create(null);for(var e,f=this.makeBaseEvent(a,d),g=0;g<b.length;g++)e=b[g],f[e]=d[e]||c[g];f.buttons=d.buttons||0;var h=0;return h=d.pressure?d.pressure:f.buttons?.5:0,f.x=f.clientX,f.y=f.clientY,f.pointerId=d.pointerId||0,f.width=d.width||0,f.height=d.height||0,f.pressure=h,f.tiltX=d.tiltX||0,f.tiltY=d.tiltY||0,f.pointerType=d.pointerType||"",f.hwTimestamp=d.hwTimestamp||0,f.isPrimary=d.isPrimary||!1,f._source=d._source||"",f}};a.eventFactory=e}(window.PolymerGestures),function(a){function b(){if(c){var a=new Map;return a.pointers=d,a}this.keys=[],this.values=[]}var c=window.Map&&window.Map.prototype.forEach,d=function(){return this.size};b.prototype={set:function(a,b){var c=this.keys.indexOf(a);c>-1?this.values[c]=b:(this.keys.push(a),this.values.push(b))},has:function(a){return this.keys.indexOf(a)>-1},"delete":function(a){var b=this.keys.indexOf(a);b>-1&&(this.keys.splice(b,1),this.values.splice(b,1))},get:function(a){var b=this.keys.indexOf(a);return this.values[b]},clear:function(){this.keys.length=0,this.values.length=0},forEach:function(a,b){this.values.forEach(function(c,d){a.call(b,c,this.keys[d],this)},this)},pointers:function(){return this.keys.length}},a.PointerMap=b}(window.PolymerGestures),function(a){var b,c=["bubbles","cancelable","view","detail","screenX","screenY","clientX","clientY","ctrlKey","altKey","shiftKey","metaKey","button","relatedTarget","buttons","pointerId","width","height","pressure","tiltX","tiltY","pointerType","hwTimestamp","isPrimary","type","target","currentTarget","which","pageX","pageY","timeStamp","preventTap","tapPrevented","_source"],d=[!1,!1,null,null,0,0,0,0,!1,!1,!1,!1,0,null,0,0,0,0,0,0,0,"",0,!1,"",null,null,0,0,0,0,function(){},!1],e="undefined"!=typeof SVGElementInstance,f=a.eventFactory,g={IS_IOS:!1,pointermap:new a.PointerMap,requiredGestures:new a.PointerMap,eventMap:Object.create(null),eventSources:Object.create(null),eventSourceList:[],gestures:[],dependencyMap:{down:{listeners:0,index:-1},up:{listeners:0,index:-1}},gestureQueue:[],registerSource:function(a,b){var c=b,d=c.events;d&&(d.forEach(function(a){c[a]&&(this.eventMap[a]=c[a].bind(c))},this),this.eventSources[a]=c,this.eventSourceList.push(c))},registerGesture:function(a,b){var c=Object.create(null);c.listeners=0,c.index=this.gestures.length;for(var d,e=0;e<b.exposes.length;e++)d=b.exposes[e].toLowerCase(),this.dependencyMap[d]=c;this.gestures.push(b)},register:function(a,b){for(var c,d=this.eventSourceList.length,e=0;d>e&&(c=this.eventSourceList[e]);e++)c.register.call(c,a,b)},unregister:function(a){for(var b,c=this.eventSourceList.length,d=0;c>d&&(b=this.eventSourceList[d]);d++)b.unregister.call(b,a)},down:function(a){this.requiredGestures.set(a.pointerId,b),this.fireEvent("down",a)},move:function(a){a.type="move",this.fillGestureQueue(a)},up:function(a){this.fireEvent("up",a),this.requiredGestures.delete(a.pointerId)},cancel:function(a){a.tapPrevented=!0,this.fireEvent("up",a),this.requiredGestures.delete(a.pointerId)},addGestureDependency:function(a,b){var c=a._pgEvents;if(c)for(var d,e,f,g=Object.keys(c),h=0;h<g.length;h++)f=g[h],c[f]>0&&(d=this.dependencyMap[f],e=d?d.index:-1,b[e]=!0)},eventHandler:function(c){var d=c.type;if("touchstart"===d||"mousedown"===d||"pointerdown"===d||"MSPointerDown"===d)if(c._handledByPG||(b={}),this.IS_IOS)for(var e,f=a.targetFinding.path(c),g=0;g<f.length;g++)e=f[g],this.addGestureDependency(e,b);else this.addGestureDependency(c.currentTarget,b);if(!c._handledByPG){var h=this.eventMap&&this.eventMap[d];h&&h(c),c._handledByPG=!0}},listen:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.addEvent(a,c)},unlisten:function(a,b){for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.removeEvent(a,c)},addEvent:function(a,b){a.addEventListener(b,this.boundHandler)},removeEvent:function(a,b){a.removeEventListener(b,this.boundHandler)},makeEvent:function(a,b){var c=f.makePointerEvent(a,b);return c.preventDefault=b.preventDefault,c.tapPrevented=b.tapPrevented,c._target=c._target||b.target,c},fireEvent:function(a,b){var c=this.makeEvent(a,b);return this.dispatchEvent(c)},cloneEvent:function(a){for(var b,f=Object.create(null),g=0;g<c.length;g++)b=c[g],f[b]=a[b]||d[g],("target"===b||"relatedTarget"===b)&&e&&f[b]instanceof SVGElementInstance&&(f[b]=f[b].correspondingUseElement);return f.preventDefault=function(){a.preventDefault()},f},dispatchEvent:function(a){var b=a._target;if(b){b.dispatchEvent(a);var c=this.cloneEvent(a);c.target=b,this.fillGestureQueue(c)}},gestureTrigger:function(){for(var a,b,c=0;c<this.gestureQueue.length;c++){a=this.gestureQueue[c],b=a._requiredGestures;for(var d,e,f=0;f<this.gestures.length;f++)b[f]&&(d=this.gestures[f],e=d[a.type],e&&e.call(d,a))}this.gestureQueue.length=0},fillGestureQueue:function(a){this.gestureQueue.length||requestAnimationFrame(this.boundGestureTrigger),a._requiredGestures=this.requiredGestures.get(a.pointerId),this.gestureQueue.push(a)}};g.boundHandler=g.eventHandler.bind(g),g.boundGestureTrigger=g.gestureTrigger.bind(g),a.dispatcher=g,a.activateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];if(d){var e=g.gestures[d.index];if(a._pgListeners||(g.register(a),a._pgListeners=0),e){var f,h=e.defaultActions&&e.defaultActions[c];switch(a.nodeType){case Node.ELEMENT_NODE:f=a;break;case Node.DOCUMENT_FRAGMENT_NODE:f=a.host;break;default:f=null}h&&f&&!f.hasAttribute("touch-action")&&f.setAttribute("touch-action",h)}a._pgEvents||(a._pgEvents={}),a._pgEvents[c]=(a._pgEvents[c]||0)+1,a._pgListeners++}return Boolean(d)},a.addEventListener=function(b,c,d,e){d&&(a.activateGesture(b,c),b.addEventListener(c,d,e))},a.deactivateGesture=function(a,b){var c=b.toLowerCase(),d=g.dependencyMap[c];return d&&(a._pgListeners>0&&a._pgListeners--,0===a._pgListeners&&g.unregister(a),a._pgEvents&&(a._pgEvents[c]>0?a._pgEvents[c]--:a._pgEvents[c]=0)),Boolean(d)},a.removeEventListener=function(b,c,d,e){d&&(a.deactivateGesture(b,c),b.removeEventListener(c,d,e))}}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=25,e=[0,1,4,2],f=!1;try{f=1===new MouseEvent("test",{buttons:1}).buttons}catch(g){}var h={POINTER_ID:1,POINTER_TYPE:"mouse",events:["mousedown","mousemove","mouseup"],exposes:["down","up","move"],register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},lastTouches:[],isEventSimulatedFromTouch:function(a){for(var b,c=this.lastTouches,e=a.clientX,f=a.clientY,g=0,h=c.length;h>g&&(b=c[g]);g++){var i=Math.abs(e-b.x),j=Math.abs(f-b.y);if(d>=i&&d>=j)return!0}},prepareEvent:function(a){var c=b.cloneEvent(a);return c.pointerId=this.POINTER_ID,c.isPrimary=!0,c.pointerType=this.POINTER_TYPE,c._source="mouse",f||(c.buttons=e[c.which]||0),c},mousedown:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=c.has(this.POINTER_ID);e&&this.mouseup(d);var f=this.prepareEvent(d);f.target=a.findTarget(d),c.set(this.POINTER_ID,f.target),b.down(f)}},mousemove:function(a){if(!this.isEventSimulatedFromTouch(a)){var d=c.get(this.POINTER_ID);if(d){var e=this.prepareEvent(a);e.target=d,0===e.buttons?(b.cancel(e),this.cleanupMouse()):b.move(e)}}},mouseup:function(d){if(!this.isEventSimulatedFromTouch(d)){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(this.POINTER_ID),b.up(e),this.cleanupMouse()}},cleanupMouse:function(){c["delete"](this.POINTER_ID)}};a.mouseEvents=h}(window.PolymerGestures),function(a){var b=a.dispatcher,c=(a.targetFinding.allShadows.bind(a.targetFinding),b.pointermap),d=(Array.prototype.map.call.bind(Array.prototype.map),2500),e=200,f=20,g=!1,h={IS_IOS:!1,events:["touchstart","touchmove","touchend","touchcancel"],exposes:["down","up","move"],register:function(a,c){(this.IS_IOS?c:!c)&&b.listen(a,this.events)},unregister:function(a){this.IS_IOS||b.unlisten(a,this.events)},scrollTypes:{EMITTER:"none",XSCROLLER:"pan-x",YSCROLLER:"pan-y"},touchActionToScrollType:function(a){var b=a,c=this.scrollTypes;return b===c.EMITTER?"none":b===c.XSCROLLER?"X":b===c.YSCROLLER?"Y":"XY"},POINTER_TYPE:"touch",firstTouch:null,isPrimaryTouch:function(a){return this.firstTouch===a.identifier},setPrimaryTouch:function(a){(0===c.pointers()||1===c.pointers()&&c.has(1))&&(this.firstTouch=a.identifier,this.firstXY={X:a.clientX,Y:a.clientY},this.scrolling=null,this.cancelResetClickCount())},removePrimaryPointer:function(a){a.isPrimary&&(this.firstTouch=null,this.firstXY=null,this.resetClickCount())},clickCount:0,resetId:null,resetClickCount:function(){var a=function(){this.clickCount=0,this.resetId=null}.bind(this);this.resetId=setTimeout(a,e)},cancelResetClickCount:function(){this.resetId&&clearTimeout(this.resetId)},typeToButtons:function(a){var b=0;return("touchstart"===a||"touchmove"===a)&&(b=1),b},findTarget:function(b,d){if("touchstart"===this.currentTouchEvent.type){if(this.isPrimaryTouch(b)){var e={clientX:b.clientX,clientY:b.clientY,path:this.currentTouchEvent.path,target:this.currentTouchEvent.target};return a.findTarget(e)}return a.findTarget(b)}return c.get(d)},touchToPointer:function(a){var c=this.currentTouchEvent,d=b.cloneEvent(a),e=d.pointerId=a.identifier+2;d.target=this.findTarget(a,e),d.bubbles=!0,d.cancelable=!0,d.detail=this.clickCount,d.buttons=this.typeToButtons(c.type),d.width=a.webkitRadiusX||a.radiusX||0,d.height=a.webkitRadiusY||a.radiusY||0,d.pressure=a.webkitForce||a.force||.5,d.isPrimary=this.isPrimaryTouch(a),d.pointerType=this.POINTER_TYPE,d._source="touch";var f=this;return d.preventDefault=function(){f.scrolling=!1,f.firstXY=null,c.preventDefault()},d},processTouches:function(a,b){var d=a.changedTouches;this.currentTouchEvent=a;for(var e,f,g=0;g<d.length;g++)e=d[g],f=this.touchToPointer(e),"touchstart"===a.type&&c.set(f.pointerId,f.target),c.has(f.pointerId)&&b.call(this,f),("touchend"===a.type||a._cancel)&&this.cleanUpPointer(f)},shouldScroll:function(b){if(this.firstXY){var c,d=a.targetFinding.findTouchAction(b),e=this.touchActionToScrollType(d);if("none"===e)c=!1;else if("XY"===e)c=!0;else{var f=b.changedTouches[0],g=e,h="Y"===e?"X":"Y",i=Math.abs(f["client"+g]-this.firstXY[g]),j=Math.abs(f["client"+h]-this.firstXY[h]);c=i>=j}return c}},findTouch:function(a,b){for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)if(c.identifier===b)return!0},vacuumTouches:function(a){var b=a.touches;if(c.pointers()>=b.length){var d=[];c.forEach(function(a,c){if(1!==c&&!this.findTouch(b,c-2)){var e=a;d.push(e)}},this),d.forEach(function(a){this.cancel(a),c.delete(a.pointerId)})}},touchstart:function(a){this.vacuumTouches(a),this.setPrimaryTouch(a.changedTouches[0]),this.dedupSynthMouse(a),this.scrolling||(this.clickCount++,this.processTouches(a,this.down))},down:function(a){b.down(a)},touchmove:function(a){if(g)a.cancelable&&this.processTouches(a,this.move);else if(this.scrolling){if(this.firstXY){var b=a.changedTouches[0],c=b.clientX-this.firstXY.X,d=b.clientY-this.firstXY.Y,e=Math.sqrt(c*c+d*d);e>=f&&(this.touchcancel(a),this.scrolling=!0,this.firstXY=null)}}else null===this.scrolling&&this.shouldScroll(a)?this.scrolling=!0:(this.scrolling=!1,a.preventDefault(),this.processTouches(a,this.move))},move:function(a){b.move(a)},touchend:function(a){this.dedupSynthMouse(a),this.processTouches(a,this.up)},up:function(c){c.relatedTarget=a.findTarget(c),b.up(c)},cancel:function(a){b.cancel(a)},touchcancel:function(a){a._cancel=!0,this.processTouches(a,this.cancel)},cleanUpPointer:function(a){c["delete"](a.pointerId),this.removePrimaryPointer(a)},dedupSynthMouse:function(b){var c=a.mouseEvents.lastTouches,e=b.changedTouches[0];if(this.isPrimaryTouch(e)){var f={x:e.clientX,y:e.clientY};c.push(f);var g=function(a,b){var c=a.indexOf(b);c>-1&&a.splice(c,1)}.bind(null,c,f);setTimeout(g,d)}}};a.touchEvents=h}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d=window.MSPointerEvent&&"number"==typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE,e={events:["MSPointerDown","MSPointerMove","MSPointerUp","MSPointerCancel"],register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},POINTER_TYPES:["","unavailable","touch","pen","mouse"],prepareEvent:function(a){var c=a;return c=b.cloneEvent(a),d&&(c.pointerType=this.POINTER_TYPES[a.pointerType]),c._source="ms",c},cleanup:function(a){c["delete"](a)},MSPointerDown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(d.pointerId,e.target),b.down(e)},MSPointerMove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},MSPointerUp:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},MSPointerCancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.msEvents=e}(window.PolymerGestures),function(a){var b=a.dispatcher,c=b.pointermap,d={events:["pointerdown","pointermove","pointerup","pointercancel"],prepareEvent:function(a){var c=b.cloneEvent(a);return c._source="pointer",c},register:function(a){b.listen(a,this.events)},unregister:function(a){a!==document&&b.unlisten(a,this.events)},cleanup:function(a){c["delete"](a)},pointerdown:function(d){var e=this.prepareEvent(d);e.target=a.findTarget(d),c.set(e.pointerId,e.target),b.down(e)},pointermove:function(a){var d=c.get(a.pointerId);if(d){var e=this.prepareEvent(a);e.target=d,b.move(e)}},pointerup:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.up(e),this.cleanup(d.pointerId)},pointercancel:function(d){var e=this.prepareEvent(d);e.relatedTarget=a.findTarget(d),e.target=c.get(e.pointerId),b.cancel(e),this.cleanup(d.pointerId)}};a.pointerEvents=d}(window.PolymerGestures),function(a){var b=a.dispatcher,c=window.navigator;window.PointerEvent?b.registerSource("pointer",a.pointerEvents):c.msPointerEnabled?b.registerSource("ms",a.msEvents):(b.registerSource("mouse",a.mouseEvents),void 0!==window.ontouchstart&&b.registerSource("touch",a.touchEvents));var d=navigator.userAgent,e=d.match(/iPad|iPhone|iPod/)&&"ontouchstart"in window;b.IS_IOS=e,a.touchEvents.IS_IOS=e,b.register(document,!0)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","move","up"],exposes:["trackstart","track","trackx","tracky","trackend"],defaultActions:{track:"none",trackx:"pan-y",tracky:"pan-x"},WIGGLE_THRESHOLD:4,clampDir:function(a){return a>0?1:-1},calcPositionDelta:function(a,b){var c=0,d=0;return a&&b&&(c=b.pageX-a.pageX,d=b.pageY-a.pageY),{x:c,y:d}},fireTrack:function(a,b,d){var e=d,f=this.calcPositionDelta(e.downEvent,b),g=this.calcPositionDelta(e.lastMoveEvent,b);if(g.x)e.xDirection=this.clampDir(g.x);else if("trackx"===a)return;if(g.y)e.yDirection=this.clampDir(g.y);else if("tracky"===a)return;var h={bubbles:!0,cancelable:!0,trackInfo:e.trackInfo,relatedTarget:b.relatedTarget,pointerType:b.pointerType,pointerId:b.pointerId,_source:"track"};"tracky"!==a&&(h.x=b.x,h.dx=f.x,h.ddx=g.x,h.clientX=b.clientX,h.pageX=b.pageX,h.screenX=b.screenX,h.xDirection=e.xDirection),"trackx"!==a&&(h.dy=f.y,h.ddy=g.y,h.y=b.y,h.clientY=b.clientY,h.pageY=b.pageY,h.screenY=b.screenY,h.yDirection=e.yDirection);var i=c.makeGestureEvent(a,h);e.downTarget.dispatchEvent(i)},down:function(a){if(a.isPrimary&&("mouse"===a.pointerType?1===a.buttons:!0)){var b={downEvent:a,downTarget:a.target,trackInfo:{},lastMoveEvent:null,xDirection:0,yDirection:0,tracking:!1};d.set(a.pointerId,b)}},move:function(a){var b=d.get(a.pointerId);if(b){if(!b.tracking){var c=this.calcPositionDelta(b.downEvent,a),e=c.x*c.x+c.y*c.y;e>this.WIGGLE_THRESHOLD&&(b.tracking=!0,b.lastMoveEvent=b.downEvent,this.fireTrack("trackstart",a,b))}b.tracking&&(this.fireTrack("track",a,b),this.fireTrack("trackx",a,b),this.fireTrack("tracky",a,b)),b.lastMoveEvent=a}},up:function(a){var b=d.get(a.pointerId);b&&(b.tracking&&this.fireTrack("trackend",a,b),d.delete(a.pointerId))}};b.registerGesture("track",e)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d={HOLD_DELAY:200,WIGGLE_THRESHOLD:16,events:["down","move","up"],exposes:["hold","holdpulse","release"],heldPointer:null,holdJob:null,pulse:function(){var a=Date.now()-this.heldPointer.timeStamp,b=this.held?"holdpulse":"hold";this.fireHold(b,a),this.held=!0},cancel:function(){clearInterval(this.holdJob),this.held&&this.fireHold("release"),this.held=!1,this.heldPointer=null,this.target=null,this.holdJob=null},down:function(a){a.isPrimary&&!this.heldPointer&&(this.heldPointer=a,this.target=a.target,this.holdJob=setInterval(this.pulse.bind(this),this.HOLD_DELAY))},up:function(a){this.heldPointer&&this.heldPointer.pointerId===a.pointerId&&this.cancel()},move:function(a){if(this.heldPointer&&this.heldPointer.pointerId===a.pointerId){var b=a.clientX-this.heldPointer.clientX,c=a.clientY-this.heldPointer.clientY;b*b+c*c>this.WIGGLE_THRESHOLD&&this.cancel()}},fireHold:function(a,b){var d={bubbles:!0,cancelable:!0,pointerType:this.heldPointer.pointerType,pointerId:this.heldPointer.pointerId,x:this.heldPointer.clientX,y:this.heldPointer.clientY,_source:"hold"};b&&(d.holdTime=b);var e=c.makeGestureEvent(a,d);this.target.dispatchEvent(e)}};b.registerGesture("hold",d)}(window.PolymerGestures),function(a){var b=a.dispatcher,c=a.eventFactory,d=new a.PointerMap,e={events:["down","up"],exposes:["tap"],down:function(a){a.isPrimary&&!a.tapPrevented&&d.set(a.pointerId,{target:a.target,buttons:a.buttons,x:a.clientX,y:a.clientY})},shouldTap:function(a,b){return"mouse"===a.pointerType?1===b.buttons:!a.tapPrevented},up:function(b){var e=d.get(b.pointerId);if(e&&this.shouldTap(b,e)){var f=a.targetFinding.LCA(e.target,b.relatedTarget);if(f){var g=c.makeGestureEvent("tap",{bubbles:!0,cancelable:!0,x:b.clientX,y:b.clientY,detail:b.detail,pointerType:b.pointerType,pointerId:b.pointerId,altKey:b.altKey,ctrlKey:b.ctrlKey,metaKey:b.metaKey,shiftKey:b.shiftKey,_source:"tap"});f.dispatchEvent(g)}}d.delete(b.pointerId)}};c.preventTap=function(a){return function(){a.tapPrevented=!0,d.delete(a.pointerId)}},b.registerGesture("tap",e)}(window.PolymerGestures),function(a){"use strict";function b(a,b){if(!a)throw new Error("ASSERT: "+b)}function c(a){return a>=48&&57>=a}function d(a){return 32===a||9===a||11===a||12===a||160===a||a>=5760&&"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff".indexOf(String.fromCharCode(a))>0}function e(a){return 10===a||13===a||8232===a||8233===a}function f(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a}function g(a){return 36===a||95===a||a>=65&&90>=a||a>=97&&122>=a||a>=48&&57>=a}function h(a){return"this"===a}function i(){for(;Y>X&&d(W.charCodeAt(X));)++X}function j(){var a,b;for(a=X++;Y>X&&(b=W.charCodeAt(X),g(b));)++X;return W.slice(a,X)}function k(){var a,b,c;return a=X,b=j(),c=1===b.length?S.Identifier:h(b)?S.Keyword:"null"===b?S.NullLiteral:"true"===b||"false"===b?S.BooleanLiteral:S.Identifier,{type:c,value:b,range:[a,X]}}function l(){var a,b,c=X,d=W.charCodeAt(X),e=W[X];switch(d){case 46:case 40:case 41:case 59:case 44:case 123:case 125:case 91:case 93:case 58:case 63:return++X,{type:S.Punctuator,value:String.fromCharCode(d),range:[c,X]};default:if(a=W.charCodeAt(X+1),61===a)switch(d){case 37:case 38:case 42:case 43:case 45:case 47:case 60:case 62:case 124:return X+=2,{type:S.Punctuator,value:String.fromCharCode(d)+String.fromCharCode(a),range:[c,X]};case 33:case 61:return X+=2,61===W.charCodeAt(X)&&++X,{type:S.Punctuator,value:W.slice(c,X),range:[c,X]}}}return b=W[X+1],e===b&&"&|".indexOf(e)>=0?(X+=2,{type:S.Punctuator,value:e+b,range:[c,X]}):"<>=!+-*%&|^/".indexOf(e)>=0?(++X,{type:S.Punctuator,value:e,range:[c,X]}):void s({},V.UnexpectedToken,"ILLEGAL")}function m(){var a,d,e;if(e=W[X],b(c(e.charCodeAt(0))||"."===e,"Numeric literal must start with a decimal digit or a decimal point"),d=X,a="","."!==e){for(a=W[X++],e=W[X],"0"===a&&e&&c(e.charCodeAt(0))&&s({},V.UnexpectedToken,"ILLEGAL");c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("."===e){for(a+=W[X++];c(W.charCodeAt(X));)a+=W[X++];e=W[X]}if("e"===e||"E"===e)if(a+=W[X++],e=W[X],("+"===e||"-"===e)&&(a+=W[X++]),c(W.charCodeAt(X)))for(;c(W.charCodeAt(X));)a+=W[X++];else s({},V.UnexpectedToken,"ILLEGAL");return f(W.charCodeAt(X))&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.NumericLiteral,value:parseFloat(a),range:[d,X]}}function n(){var a,c,d,f="",g=!1;for(a=W[X],b("'"===a||'"'===a,"String literal must starts with a quote"),c=X,++X;Y>X;){if(d=W[X++],d===a){a="";break}if("\\"===d)if(d=W[X++],d&&e(d.charCodeAt(0)))"\r"===d&&"\n"===W[X]&&++X;else switch(d){case"n":f+="\n";break;case"r":f+="\r";break;case"t":f+=" ";break;case"b":f+="\b";break;case"f":f+="\f";break;case"v":f+="";break;default:f+=d}else{if(e(d.charCodeAt(0)))break;f+=d}}return""!==a&&s({},V.UnexpectedToken,"ILLEGAL"),{type:S.StringLiteral,value:f,octal:g,range:[c,X]}}function o(a){return a.type===S.Identifier||a.type===S.Keyword||a.type===S.BooleanLiteral||a.type===S.NullLiteral}function p(){var a;return i(),X>=Y?{type:S.EOF,range:[X,X]}:(a=W.charCodeAt(X),40===a||41===a||58===a?l():39===a||34===a?n():f(a)?k():46===a?c(W.charCodeAt(X+1))?m():l():c(a)?m():l())}function q(){var a;return a=$,X=a.range[1],$=p(),X=a.range[1],a}function r(){var a;a=X,$=p(),X=a}function s(a,c){var d,e=Array.prototype.slice.call(arguments,2),f=c.replace(/%(\d)/g,function(a,c){return b(c<e.length,"Message reference must be in range"),e[c]});throw d=new Error(f),d.index=X,d.description=f,d}function t(a){s(a,V.UnexpectedToken,a.value)}function u(a){var b=q();(b.type!==S.Punctuator||b.value!==a)&&t(b)}function v(a){return $.type===S.Punctuator&&$.value===a}function w(a){return $.type===S.Keyword&&$.value===a}function x(){var a=[];for(u("[");!v("]");)v(",")?(q(),a.push(null)):(a.push(bb()),v("]")||u(","));return u("]"),Z.createArrayExpression(a)}function y(){var a;return i(),a=q(),a.type===S.StringLiteral||a.type===S.NumericLiteral?Z.createLiteral(a):Z.createIdentifier(a.value)}function z(){var a,b;return a=$,i(),(a.type===S.EOF||a.type===S.Punctuator)&&t(a),b=y(),u(":"),Z.createProperty("init",b,bb())}function A(){var a=[];for(u("{");!v("}");)a.push(z()),v("}")||u(",");return u("}"),Z.createObjectExpression(a)}function B(){var a;return u("("),a=bb(),u(")"),a}function C(){var a,b,c;return v("(")?B():(a=$.type,a===S.Identifier?c=Z.createIdentifier(q().value):a===S.StringLiteral||a===S.NumericLiteral?c=Z.createLiteral(q()):a===S.Keyword?w("this")&&(q(),c=Z.createThisExpression()):a===S.BooleanLiteral?(b=q(),b.value="true"===b.value,c=Z.createLiteral(b)):a===S.NullLiteral?(b=q(),b.value=null,c=Z.createLiteral(b)):v("[")?c=x():v("{")&&(c=A()),c?c:void t(q()))}function D(){var a=[];if(u("("),!v(")"))for(;Y>X&&(a.push(bb()),!v(")"));)u(",");return u(")"),a}function E(){var a;return a=q(),o(a)||t(a),Z.createIdentifier(a.value)}function F(){return u("."),E()}function G(){var a;return u("["),a=bb(),u("]"),a}function H(){var a,b,c;for(a=C();;)if(v("["))c=G(),a=Z.createMemberExpression("[",a,c);else if(v("."))c=F(),a=Z.createMemberExpression(".",a,c);else{if(!v("("))break;b=D(),a=Z.createCallExpression(a,b)}return a}function I(){var a,b;return $.type!==S.Punctuator&&$.type!==S.Keyword?b=ab():v("+")||v("-")||v("!")?(a=q(),b=I(),b=Z.createUnaryExpression(a.value,b)):w("delete")||w("void")||w("typeof")?s({},V.UnexpectedToken):b=ab(),b}function J(a){var b=0;if(a.type!==S.Punctuator&&a.type!==S.Keyword)return 0;switch(a.value){case"||":b=1;break;case"&&":b=2;break;case"==":case"!=":case"===":case"!==":b=6;break;case"<":case">":case"<=":case">=":case"instanceof":b=7;break;case"in":b=7;break;case"+":case"-":b=9;break;case"*":case"/":case"%":b=11}return b}function K(){var a,b,c,d,e,f,g,h;if(g=I(),b=$,c=J(b),0===c)return g;for(b.prec=c,q(),e=I(),d=[g,b,e];(c=J($))>0;){for(;d.length>2&&c<=d[d.length-2].prec;)e=d.pop(),f=d.pop().value,g=d.pop(),a=Z.createBinaryExpression(f,g,e),d.push(a);b=q(),b.prec=c,d.push(b),a=I(),d.push(a)}for(h=d.length-1,a=d[h];h>1;)a=Z.createBinaryExpression(d[h-1].value,d[h-2],a),h-=2;return a}function L(){var a,b,c;return a=K(),v("?")&&(q(),b=L(),u(":"),c=L(),a=Z.createConditionalExpression(a,b,c)),a}function M(){var a,b;return a=q(),a.type!==S.Identifier&&t(a),b=v("(")?D():[],Z.createFilter(a.value,b)}function N(){for(;v("|");)q(),M()}function O(){i(),r();var a=bb();a&&(","===$.value||"in"==$.value&&a.type===U.Identifier?Q(a):(N(),"as"===$.value?P(a):Z.createTopLevel(a))),$.type!==S.EOF&&t($)}function P(a){q();var b=q().value;Z.createAsExpression(a,b)}function Q(a){var b;","===$.value&&(q(),$.type!==S.Identifier&&t($),b=q().value),q();var c=bb();N(),Z.createInExpression(a.name,b,c)}function R(a,b){return Z=b,W=a,X=0,Y=W.length,$=null,_={labelSet:{}},O()}var S,T,U,V,W,X,Y,Z,$,_;S={BooleanLiteral:1,EOF:2,Identifier:3,Keyword:4,NullLiteral:5,NumericLiteral:6,Punctuator:7,StringLiteral:8},T={},T[S.BooleanLiteral]="Boolean",T[S.EOF]="<end>",T[S.Identifier]="Identifier",T[S.Keyword]="Keyword",T[S.NullLiteral]="Null",T[S.NumericLiteral]="Numeric",T[S.Punctuator]="Punctuator",T[S.StringLiteral]="String",U={ArrayExpression:"ArrayExpression",BinaryExpression:"BinaryExpression",CallExpression:"CallExpression",ConditionalExpression:"ConditionalExpression",EmptyStatement:"EmptyStatement",ExpressionStatement:"ExpressionStatement",Identifier:"Identifier",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",ObjectExpression:"ObjectExpression",Program:"Program",Property:"Property",ThisExpression:"ThisExpression",UnaryExpression:"UnaryExpression"},V={UnexpectedToken:"Unexpected token %0",UnknownLabel:"Undefined label '%0'",Redeclaration:"%0 '%1' has already been declared"};var ab=H,bb=L;a.esprima={parse:R}}(this),function(a){"use strict";function b(a,b,d,e){var f;try{if(f=c(a),f.scopeIdent&&(d.nodeType!==Node.ELEMENT_NODE||"TEMPLATE"!==d.tagName||"bind"!==b&&"repeat"!==b))throw Error("as and in can only be used within <template bind/repeat>")}catch(g){return void console.error("Invalid expression syntax: "+a,g)}return function(a,b,c){var d=f.getBinding(a,e,c);return f.scopeIdent&&d&&(b.polymerExpressionScopeIdent_=f.scopeIdent,f.indexIdent&&(b.polymerExpressionIndexIdent_=f.indexIdent)),d}}function c(a){var b=q[a];if(!b){var c=new j;esprima.parse(a,c),b=new l(c),q[a]=b}return b}function d(a){this.value=a,this.valueFn_=void 0}function e(a){this.name=a,this.path=Path.get(a)}function f(a,b,c){this.computed="["==c,this.dynamicDeps="function"==typeof a||a.dynamicDeps||this.computed&&!(b instanceof d),this.simplePath=!this.dynamicDeps&&(b instanceof e||b instanceof d)&&(a instanceof f||a instanceof e),this.object=this.simplePath?a:i(a),this.property=!this.computed||this.simplePath?b:i(b)}function g(a,b){this.name=a,this.args=[];for(var c=0;c<b.length;c++)this.args[c]=i(b[c])}function h(){throw Error("Not Implemented")}function i(a){return"function"==typeof a?a:a.valueFn()}function j(){this.expression=null,this.filters=[],this.deps={},this.currentPath=void 0,this.scopeIdent=void 0,this.indexIdent=void 0,this.dynamicDeps=!1}function k(a){this.value_=a}function l(a){if(this.scopeIdent=a.scopeIdent,this.indexIdent=a.indexIdent,!a.expression)throw Error("No expression found.");this.expression=a.expression,i(this.expression),this.filters=a.filters,this.dynamicDeps=a.dynamicDeps}function m(a){return String(a).replace(/[A-Z]/g,function(a){return"-"+a.toLowerCase()})}function n(a,b){for(;a[t]&&!Object.prototype.hasOwnProperty.call(a,b);)a=a[t];return a}function o(a){switch(a){case"":return!1;case"false":case"null":case"true":return!0}return isNaN(Number(a))?!1:!0}function p(){}var q=Object.create(null);d.prototype={valueFn:function(){if(!this.valueFn_){var a=this.value;this.valueFn_=function(){return a}}return this.valueFn_}},e.prototype={valueFn:function(){if(!this.valueFn_){var a=(this.name,this.path);this.valueFn_=function(b,c){return c&&c.addPath(b,a),a.getValueFrom(b)}}return this.valueFn_},setValue:function(a,b){return 1==this.path.length,a=n(a,this.path[0]),this.path.setValueFrom(a,b)}},f.prototype={get fullPath(){if(!this.fullPath_){var a=this.object instanceof f?this.object.fullPath.slice():[this.object.name];
|
| -a.push(this.property instanceof e?this.property.name:this.property.value),this.fullPath_=Path.get(a)}return this.fullPath_},valueFn:function(){if(!this.valueFn_){var a=this.object;if(this.simplePath){var b=this.fullPath;this.valueFn_=function(a,c){return c&&c.addPath(a,b),b.getValueFrom(a)}}else if(this.computed){var c=this.property;this.valueFn_=function(b,d,e){var f=a(b,d,e),g=c(b,d,e);return d&&d.addPath(f,[g]),f?f[g]:void 0}}else{var b=Path.get(this.property.name);this.valueFn_=function(c,d,e){var f=a(c,d,e);return d&&d.addPath(f,b),b.getValueFrom(f)}}}return this.valueFn_},setValue:function(a,b){if(this.simplePath)return this.fullPath.setValueFrom(a,b),b;var c=this.object(a),d=this.property instanceof e?this.property.name:this.property(a);return c[d]=b}},g.prototype={transform:function(a,b,c,d,e){var f=c[this.name],g=a;if(f)g=void 0;else if(f=g[this.name],!f)return void console.error("Cannot find function or filter: "+this.name);if(d?f=f.toModel:"function"==typeof f.toDOM&&(f=f.toDOM),"function"!=typeof f)return void console.error("Cannot find function or filter: "+this.name);for(var h=e||[],j=0;j<this.args.length;j++)h.push(i(this.args[j])(a,b,c));return f.apply(g,h)}};var r={"+":function(a){return+a},"-":function(a){return-a},"!":function(a){return!a}},s={"+":function(a,b){return a+b},"-":function(a,b){return a-b},"*":function(a,b){return a*b},"/":function(a,b){return a/b},"%":function(a,b){return a%b},"<":function(a,b){return b>a},">":function(a,b){return a>b},"<=":function(a,b){return b>=a},">=":function(a,b){return a>=b},"==":function(a,b){return a==b},"!=":function(a,b){return a!=b},"===":function(a,b){return a===b},"!==":function(a,b){return a!==b},"&&":function(a,b){return a&&b},"||":function(a,b){return a||b}};j.prototype={createUnaryExpression:function(a,b){if(!r[a])throw Error("Disallowed operator: "+a);return b=i(b),function(c,d,e){return r[a](b(c,d,e))}},createBinaryExpression:function(a,b,c){if(!s[a])throw Error("Disallowed operator: "+a);switch(b=i(b),c=i(c),a){case"||":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)||c(a,d,e)};case"&&":return this.dynamicDeps=!0,function(a,d,e){return b(a,d,e)&&c(a,d,e)}}return function(d,e,f){return s[a](b(d,e,f),c(d,e,f))}},createConditionalExpression:function(a,b,c){return a=i(a),b=i(b),c=i(c),this.dynamicDeps=!0,function(d,e,f){return a(d,e,f)?b(d,e,f):c(d,e,f)}},createIdentifier:function(a){var b=new e(a);return b.type="Identifier",b},createMemberExpression:function(a,b,c){var d=new f(b,c,a);return d.dynamicDeps&&(this.dynamicDeps=!0),d},createCallExpression:function(a,b){if(!(a instanceof e))throw Error("Only identifier function invocations are allowed");var c=new g(a.name,b);return function(a,b,d){return c.transform(a,b,d,!1)}},createLiteral:function(a){return new d(a.value)},createArrayExpression:function(a){for(var b=0;b<a.length;b++)a[b]=i(a[b]);return function(b,c,d){for(var e=[],f=0;f<a.length;f++)e.push(a[f](b,c,d));return e}},createProperty:function(a,b,c){return{key:b instanceof e?b.name:b.value,value:c}},createObjectExpression:function(a){for(var b=0;b<a.length;b++)a[b].value=i(a[b].value);return function(b,c,d){for(var e={},f=0;f<a.length;f++)e[a[f].key]=a[f].value(b,c,d);return e}},createFilter:function(a,b){this.filters.push(new g(a,b))},createAsExpression:function(a,b){this.expression=a,this.scopeIdent=b},createInExpression:function(a,b,c){this.expression=c,this.scopeIdent=a,this.indexIdent=b},createTopLevel:function(a){this.expression=a},createThisExpression:h},k.prototype={open:function(){return this.value_},discardChanges:function(){return this.value_},deliver:function(){},close:function(){}},l.prototype={getBinding:function(a,b,c){function d(){if(h)return h=!1,g;i.dynamicDeps&&f.startReset();var c=i.getValue(a,i.dynamicDeps?f:void 0,b);return i.dynamicDeps&&f.finishReset(),c}function e(c){return i.setValue(a,c,b),c}if(c)return this.getValue(a,void 0,b);var f=new CompoundObserver,g=this.getValue(a,f,b),h=!0,i=this;return new ObserverTransform(f,d,e,!0)},getValue:function(a,b,c){for(var d=i(this.expression)(a,b,c),e=0;e<this.filters.length;e++)d=this.filters[e].transform(a,b,c,!1,[d]);return d},setValue:function(a,b,c){for(var d=this.filters?this.filters.length:0;d-->0;)b=this.filters[d].transform(a,void 0,c,!0,[b]);return this.expression.setValue?this.expression.setValue(a,b):void 0}};var t="@"+Math.random().toString(36).slice(2);p.prototype={styleObject:function(a){var b=[];for(var c in a)b.push(m(c)+": "+a[c]);return b.join("; ")},tokenList:function(a){var b=[];for(var c in a)a[c]&&b.push(c);return b.join(" ")},prepareInstancePositionChanged:function(a){var b=a.polymerExpressionIndexIdent_;if(b)return function(a,c){a.model[b]=c}},prepareBinding:function(a,c,d){var e=Path.get(a);{if(o(a)||!e.valid)return b(a,c,d,this);if(1==e.length)return function(a,b,c){if(c)return e.getValueFrom(a);var d=n(a,e[0]);return new PathObserver(d,e)}}},prepareInstanceModel:function(a){var b=a.polymerExpressionScopeIdent_;if(b){var c=a.templateInstance?a.templateInstance.model:a.model,d=a.polymerExpressionIndexIdent_;return function(a){return u(c,a,b,d)}}}};var u="__proto__"in{}?function(a,b,c,d){var e={};return e[c]=b,e[d]=void 0,e[t]=a,e.__proto__=a,e}:function(a,b,c,d){var e=Object.create(a);return Object.defineProperty(e,c,{value:b,configurable:!0,writable:!0}),Object.defineProperty(e,d,{value:void 0,configurable:!0,writable:!0}),Object.defineProperty(e,t,{value:a,configurable:!0,writable:!0}),e};a.PolymerExpressions=p,p.getExpression=c}(this),Polymer={version:"0.4.2-8c339cf"},"function"==typeof window.Polymer&&(Polymer={}),window.Platform||(logFlags=window.logFlags||{},Platform={flush:function(){}},CustomElements={useNative:!0,ready:!0,takeRecords:function(){},"instanceof":function(a,b){return a instanceof b}},HTMLImports={useNative:!0},addEventListener("HTMLImportsLoaded",function(){document.dispatchEvent(new CustomEvent("WebComponentsReady",{bubbles:!0}))}),ShadowDOMPolyfill=null,wrap=unwrap=function(a){return a}),function(a){function b(a,b){b=b||q,d(function(){f(a,b)},b)}function c(a){return"complete"===a.readyState||a.readyState===s}function d(a,b){if(c(b))a&&a();else{var e=function(){("complete"===b.readyState||b.readyState===s)&&(b.removeEventListener(t,e),d(a,b))};b.addEventListener(t,e)}}function e(a){a.target.__loaded=!0}function f(a,b){function c(){h==i&&a&&a()}function d(a){e(a),h++,c()}var f=b.querySelectorAll("link[rel=import]"),h=0,i=f.length;if(i)for(var j,k=0;i>k&&(j=f[k]);k++)g(j)?d.call(j,{target:j}):(j.addEventListener("load",d),j.addEventListener("error",d));else c()}function g(a){return m?a.__loaded||a.import&&"loading"!==a.import.readyState:a.__importParsed}function h(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)i(b)&&j(b)}function i(a){return"link"===a.localName&&"import"===a.rel}function j(a){var b=a.import;b?e({target:a}):(a.addEventListener("load",e),a.addEventListener("error",e))}var k="import",l=k in document.createElement("link"),m=l,n=/Trident/.test(navigator.userAgent),o=Boolean(window.ShadowDOMPolyfill),p=function(a){return o?ShadowDOMPolyfill.wrapIfNeeded(a):a},q=p(document),r={get:function(){var a=HTMLImports.currentScript||document.currentScript||("complete"!==document.readyState?document.scripts[document.scripts.length-1]:null);return p(a)},configurable:!0};Object.defineProperty(document,"_currentScript",r),Object.defineProperty(q,"_currentScript",r);var s=n?"complete":"interactive",t="readystatechange";m&&(new MutationObserver(function(a){for(var b,c=0,d=a.length;d>c&&(b=a[c]);c++)b.addedNodes&&h(b.addedNodes)}).observe(document.head,{childList:!0}),function(){if("loading"===document.readyState)for(var a,b=document.querySelectorAll("link[rel=import]"),c=0,d=b.length;d>c&&(a=b[c]);c++)j(a)}()),b(function(){HTMLImports.ready=!0,HTMLImports.readyTime=(new Date).getTime(),q.dispatchEvent(new CustomEvent("HTMLImportsLoaded",{bubbles:!0}))}),a.useNative=m,a.isImportLoaded=g,a.whenReady=b,a.rootDocument=q,a.IMPORT_LINK_TYPE=k,a.isIE=n}(window.HTMLImports),function(a){function b(a,b){return b=b||[],b.map||(b=[b]),a.apply(this,b.map(d))}function c(a,c,d){var e;switch(arguments.length){case 0:return;case 1:e=null;break;case 2:e=c.apply(this);break;default:e=b(d,c)}f[a]=e}function d(a){return f[a]}function e(a,c){HTMLImports.whenImportsReady(function(){b(c,a)})}var f={};a.marshal=d,a.modularize=c,a.using=e}(window),function(){var a=document.createElement("style");a.textContent="body {transition: opacity ease-in 0.2s; } \nbody[unresolved] {opacity: 0; display: block; overflow: hidden; } \n";var b=document.querySelector("head");b.insertBefore(a,b.firstChild)}(Platform),function(a){"use strict";function b(){function a(a){b=a}if("function"!=typeof Object.observe||"function"!=typeof Array.observe)return!1;var b=[],c={},d=[];return Object.observe(c,a),Array.observe(d,a),c.id=1,c.id=2,delete c.id,d.push(1,2),d.length=0,Object.deliverChangeRecords(a),5!==b.length?!1:"add"!=b[0].type||"update"!=b[1].type||"delete"!=b[2].type||"splice"!=b[3].type||"splice"!=b[4].type?!1:(Object.unobserve(c,a),Array.unobserve(d,a),!0)}function c(){if("undefined"!=typeof chrome&&chrome.app&&chrome.app.runtime)return!1;if("undefined"!=typeof navigator&&navigator.getDeviceStorage)return!1;try{var a=new Function("","return true;");return a()}catch(b){return!1}}function d(a){return+a===a>>>0&&""!==a}function e(a){return+a}function f(a){return a===Object(a)}function g(a,b){return a===b?0!==a||1/a===1/b:R(a)&&R(b)?!0:a!==a&&b!==b}function h(a){if(void 0===a)return"eof";var b=a.charCodeAt(0);switch(b){case 91:case 93:case 46:case 34:case 39:case 48:return a;case 95:case 36:return"ident";case 32:case 9:case 10:case 13:case 160:case 65279:case 8232:case 8233:return"ws"}return b>=97&&122>=b||b>=65&&90>=b?"ident":b>=49&&57>=b?"number":"else"}function i(){}function j(a){function b(){if(!(m>=a.length)){var b=a[m+1];return"inSingleQuote"==n&&"'"==b||"inDoubleQuote"==n&&'"'==b?(m++,d=b,o.append(),!0):void 0}}for(var c,d,e,f,g,j,k,l=[],m=-1,n="beforePath",o={push:function(){void 0!==e&&(l.push(e),e=void 0)},append:function(){void 0===e?e=d:e+=d}};n;)if(m++,c=a[m],"\\"!=c||!b(n)){if(f=h(c),k=W[n],g=k[f]||k["else"]||"error","error"==g)return;if(n=g[0],j=o[g[1]]||i,d=void 0===g[2]?c:g[2],j(),"afterPath"===n)return l}}function k(a){return V.test(a)}function l(a,b){if(b!==X)throw Error("Use Path.get to retrieve path objects");for(var c=0;c<a.length;c++)this.push(String(a[c]));Q&&this.length&&(this.getValueFrom=this.compiledGetValueFromFn())}function m(a){if(a instanceof l)return a;if((null==a||0==a.length)&&(a=""),"string"!=typeof a){if(d(a.length))return new l(a,X);a=String(a)}var b=Y[a];if(b)return b;var c=j(a);if(!c)return Z;var b=new l(c,X);return Y[a]=b,b}function n(a){return d(a)?"["+a+"]":'["'+a.replace(/"/g,'\\"')+'"]'}function o(b){for(var c=0;_>c&&b.check_();)c++;return O&&(a.dirtyCheckCycleCount=c),c>0}function p(a){for(var b in a)return!1;return!0}function q(a){return p(a.added)&&p(a.removed)&&p(a.changed)}function r(a,b){var c={},d={},e={};for(var f in b){var g=a[f];(void 0===g||g!==b[f])&&(f in a?g!==b[f]&&(e[f]=g):d[f]=void 0)}for(var f in a)f in b||(c[f]=a[f]);return Array.isArray(a)&&a.length!==b.length&&(e.length=a.length),{added:c,removed:d,changed:e}}function s(){if(!ab.length)return!1;for(var a=0;a<ab.length;a++)ab[a]();return ab.length=0,!0}function t(){function a(a){b&&b.state_===fb&&!d&&b.check_(a)}var b,c,d=!1,e=!0;return{open:function(c){if(b)throw Error("ObservedObject in use");e||Object.deliverChangeRecords(a),b=c,e=!1},observe:function(b,d){c=b,d?Array.observe(c,a):Object.observe(c,a)},deliver:function(b){d=b,Object.deliverChangeRecords(a),d=!1},close:function(){b=void 0,Object.unobserve(c,a),cb.push(this)}}}function u(a,b,c){var d=cb.pop()||t();return d.open(a),d.observe(b,c),d}function v(){function a(b,f){b&&(b===d&&(e[f]=!0),h.indexOf(b)<0&&(h.push(b),Object.observe(b,c)),a(Object.getPrototypeOf(b),f))}function b(a){for(var b=0;b<a.length;b++){var c=a[b];if(c.object!==d||e[c.name]||"setPrototype"===c.type)return!1}return!0}function c(c){if(!b(c)){for(var d,e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.iterateObjects_(a);for(var e=0;e<g.length;e++)d=g[e],d.state_==fb&&d.check_()}}var d,e,f=0,g=[],h=[],i={object:void 0,objects:h,open:function(b,c){d||(d=c,e={}),g.push(b),f++,b.iterateObjects_(a)},close:function(){if(f--,!(f>0)){for(var a=0;a<h.length;a++)Object.unobserve(h[a],c),x.unobservedCount++;g.length=0,h.length=0,d=void 0,e=void 0,db.push(this)}}};return i}function w(a,b){return $&&$.object===b||($=db.pop()||v(),$.object=b),$.open(a,b),$}function x(){this.state_=eb,this.callback_=void 0,this.target_=void 0,this.directObserver_=void 0,this.value_=void 0,this.id_=ib++}function y(a){x._allObserversCount++,kb&&jb.push(a)}function z(){x._allObserversCount--}function A(a){x.call(this),this.value_=a,this.oldObject_=void 0}function B(a){if(!Array.isArray(a))throw Error("Provided object is not an Array");A.call(this,a)}function C(a,b){x.call(this),this.object_=a,this.path_=m(b),this.directObserver_=void 0}function D(a){x.call(this),this.reportChangesOnOpen_=a,this.value_=[],this.directObserver_=void 0,this.observed_=[]}function E(a){return a}function F(a,b,c,d){this.callback_=void 0,this.target_=void 0,this.value_=void 0,this.observable_=a,this.getValueFn_=b||E,this.setValueFn_=c||E,this.dontPassThroughSet_=d}function G(a,b,c){for(var d={},e={},f=0;f<b.length;f++){var g=b[f];nb[g.type]?(g.name in c||(c[g.name]=g.oldValue),"update"!=g.type&&("add"!=g.type?g.name in d?(delete d[g.name],delete c[g.name]):e[g.name]=!0:g.name in e?delete e[g.name]:d[g.name]=!0)):(console.error("Unknown changeRecord type: "+g.type),console.error(g))}for(var h in d)d[h]=a[h];for(var h in e)e[h]=void 0;var i={};for(var h in c)if(!(h in d||h in e)){var j=a[h];c[h]!==j&&(i[h]=j)}return{added:d,removed:e,changed:i}}function H(a,b,c){return{index:a,removed:b,addedCount:c}}function I(){}function J(a,b,c,d,e,f){return sb.calcSplices(a,b,c,d,e,f)}function K(a,b,c,d){return c>b||a>d?-1:b==c||d==a?0:c>a?d>b?b-c:d-c:b>d?d-a:b-a}function L(a,b,c,d){for(var e=H(b,c,d),f=!1,g=0,h=0;h<a.length;h++){var i=a[h];if(i.index+=g,!f){var j=K(e.index,e.index+e.removed.length,i.index,i.index+i.addedCount);if(j>=0){a.splice(h,1),h--,g-=i.addedCount-i.removed.length,e.addedCount+=i.addedCount-j;var k=e.removed.length+i.removed.length-j;if(e.addedCount||k){var c=i.removed;if(e.index<i.index){var l=e.removed.slice(0,i.index-e.index);Array.prototype.push.apply(l,c),c=l}if(e.index+e.removed.length>i.index+i.addedCount){var m=e.removed.slice(i.index+i.addedCount-e.index);Array.prototype.push.apply(c,m)}e.removed=c,i.index<e.index&&(e.index=i.index)}else f=!0}else if(e.index<i.index){f=!0,a.splice(h,0,e),h++;var n=e.addedCount-e.removed.length;i.index+=n,g+=n}}}f||a.push(e)}function M(a,b){for(var c=[],f=0;f<b.length;f++){var g=b[f];switch(g.type){case"splice":L(c,g.index,g.removed.slice(),g.addedCount);break;case"add":case"update":case"delete":if(!d(g.name))continue;var h=e(g.name);if(0>h)continue;L(c,h,[g.oldValue],1);break;default:console.error("Unexpected record type: "+JSON.stringify(g))}}return c}function N(a,b){var c=[];return M(a,b).forEach(function(b){return 1==b.addedCount&&1==b.removed.length?void(b.removed[0]!==a[b.index]&&c.push(b)):void(c=c.concat(J(a,b.index,b.index+b.addedCount,b.removed,0,b.removed.length)))}),c}var O=a.testingExposeCycleCount,P=b(),Q=c(),R=a.Number.isNaN||function(b){return"number"==typeof b&&a.isNaN(b)},S="__proto__"in{}?function(a){return a}:function(a){var b=a.__proto__;if(!b)return a;var c=Object.create(b);return Object.getOwnPropertyNames(a).forEach(function(b){Object.defineProperty(c,b,Object.getOwnPropertyDescriptor(a,b))}),c},T="[$_a-zA-Z]",U="[$_a-zA-Z0-9]",V=new RegExp("^"+T+"+"+U+"*$"),W={beforePath:{ws:["beforePath"],ident:["inIdent","append"],"[":["beforeElement"],eof:["afterPath"]},inPath:{ws:["inPath"],".":["beforeIdent"],"[":["beforeElement"],eof:["afterPath"]},beforeIdent:{ws:["beforeIdent"],ident:["inIdent","append"]},inIdent:{ident:["inIdent","append"],0:["inIdent","append"],number:["inIdent","append"],ws:["inPath","push"],".":["beforeIdent","push"],"[":["beforeElement","push"],eof:["afterPath","push"]},beforeElement:{ws:["beforeElement"],0:["afterZero","append"],number:["inIndex","append"],"'":["inSingleQuote","append",""],'"':["inDoubleQuote","append",""]},afterZero:{ws:["afterElement","push"],"]":["inPath","push"]},inIndex:{0:["inIndex","append"],number:["inIndex","append"],ws:["afterElement"],"]":["inPath","push"]},inSingleQuote:{"'":["afterElement"],eof:["error"],"else":["inSingleQuote","append"]},inDoubleQuote:{'"':["afterElement"],eof:["error"],"else":["inDoubleQuote","append"]},afterElement:{ws:["afterElement"],"]":["inPath","push"]}},X={},Y={};l.get=m,l.prototype=S({__proto__:[],valid:!0,toString:function(){for(var a="",b=0;b<this.length;b++){var c=this[b];a+=k(c)?b?"."+c:c:n(c)}return a},getValueFrom:function(a){for(var b=0;b<this.length;b++){if(null==a)return;a=a[this[b]]}return a},iterateObjects:function(a,b){for(var c=0;c<this.length;c++){if(c&&(a=a[this[c-1]]),!f(a))return;b(a,this[0])}},compiledGetValueFromFn:function(){var a="",b="obj";a+="if (obj != null";for(var c,d=0;d<this.length-1;d++)c=this[d],b+=k(c)?"."+c:n(c),a+=" &&\n "+b+" != null";a+=")\n";var c=this[d];return b+=k(c)?"."+c:n(c),a+=" return "+b+";\nelse\n return undefined;",new Function("obj",a)},setValueFrom:function(a,b){if(!this.length)return!1;for(var c=0;c<this.length-1;c++){if(!f(a))return!1;a=a[this[c]]}return f(a)?(a[this[c]]=b,!0):!1}});var Z=new l("",X);Z.valid=!1,Z.getValueFrom=Z.setValueFrom=function(){};var $,_=1e3,ab=[],bb=P?function(){var a={pingPong:!0},b=!1;return Object.observe(a,function(){s(),b=!1}),function(c){ab.push(c),b||(b=!0,a.pingPong=!a.pingPong)}}():function(){return function(a){ab.push(a)}}(),cb=[],db=[],eb=0,fb=1,gb=2,hb=3,ib=1;x.prototype={open:function(a,b){if(this.state_!=eb)throw Error("Observer has already been opened.");return y(this),this.callback_=a,this.target_=b,this.connect_(),this.state_=fb,this.value_},close:function(){this.state_==fb&&(z(this),this.disconnect_(),this.value_=void 0,this.callback_=void 0,this.target_=void 0,this.state_=gb)},deliver:function(){this.state_==fb&&o(this)},report_:function(a){try{this.callback_.apply(this.target_,a)}catch(b){x._errorThrownDuringCallback=!0,console.error("Exception caught during observer callback: "+(b.stack||b))}},discardChanges:function(){return this.check_(void 0,!0),this.value_}};var jb,kb=!P;x._allObserversCount=0,kb&&(jb=[]);var lb=!1;a.Platform=a.Platform||{},a.Platform.performMicrotaskCheckpoint=function(){if(!lb&&kb){lb=!0;var b,c,d=0;do{d++,c=jb,jb=[],b=!1;for(var e=0;e<c.length;e++){var f=c[e];f.state_==fb&&(f.check_()&&(b=!0),jb.push(f))}s()&&(b=!0)}while(_>d&&b);O&&(a.dirtyCheckCycleCount=d),lb=!1}},kb&&(a.Platform.clearObservers=function(){jb=[]}),A.prototype=S({__proto__:x.prototype,arrayObserve:!1,connect_:function(){P?this.directObserver_=u(this,this.value_,this.arrayObserve):this.oldObject_=this.copyObject(this.value_)},copyObject:function(a){var b=Array.isArray(a)?[]:{};for(var c in a)b[c]=a[c];return Array.isArray(a)&&(b.length=a.length),b},check_:function(a){var b,c;if(P){if(!a)return!1;c={},b=G(this.value_,a,c)}else c=this.oldObject_,b=r(this.value_,this.oldObject_);return q(b)?!1:(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b.added||{},b.removed||{},b.changed||{},function(a){return c[a]}]),!0)},disconnect_:function(){P?(this.directObserver_.close(),this.directObserver_=void 0):this.oldObject_=void 0},deliver:function(){this.state_==fb&&(P?this.directObserver_.deliver(!1):o(this))},discardChanges:function(){return this.directObserver_?this.directObserver_.deliver(!0):this.oldObject_=this.copyObject(this.value_),this.value_}}),B.prototype=S({__proto__:A.prototype,arrayObserve:!0,copyObject:function(a){return a.slice()},check_:function(a){var b;if(P){if(!a)return!1;b=N(this.value_,a)}else b=J(this.value_,0,this.value_.length,this.oldObject_,0,this.oldObject_.length);return b&&b.length?(P||(this.oldObject_=this.copyObject(this.value_)),this.report_([b]),!0):!1}}),B.applySplices=function(a,b,c){c.forEach(function(c){for(var d=[c.index,c.removed.length],e=c.index;e<c.index+c.addedCount;)d.push(b[e]),e++;Array.prototype.splice.apply(a,d)})},C.prototype=S({__proto__:x.prototype,get path(){return this.path_},connect_:function(){P&&(this.directObserver_=w(this,this.object_)),this.check_(void 0,!0)},disconnect_:function(){this.value_=void 0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},iterateObjects_:function(a){this.path_.iterateObjects(this.object_,a)},check_:function(a,b){var c=this.value_;return this.value_=this.path_.getValueFrom(this.object_),b||g(this.value_,c)?!1:(this.report_([this.value_,c,this]),!0)},setValue:function(a){this.path_&&this.path_.setValueFrom(this.object_,a)}});var mb={};D.prototype=S({__proto__:x.prototype,connect_:function(){if(P){for(var a,b=!1,c=0;c<this.observed_.length;c+=2)if(a=this.observed_[c],a!==mb){b=!0;break}b&&(this.directObserver_=w(this,a))}this.check_(void 0,!this.reportChangesOnOpen_)},disconnect_:function(){for(var a=0;a<this.observed_.length;a+=2)this.observed_[a]===mb&&this.observed_[a+1].close();this.observed_.length=0,this.value_.length=0,this.directObserver_&&(this.directObserver_.close(this),this.directObserver_=void 0)},addPath:function(a,b){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add paths once started.");var b=m(b);if(this.observed_.push(a,b),this.reportChangesOnOpen_){var c=this.observed_.length/2-1;this.value_[c]=b.getValueFrom(a)}},addObserver:function(a){if(this.state_!=eb&&this.state_!=hb)throw Error("Cannot add observers once started.");if(this.observed_.push(mb,a),this.reportChangesOnOpen_){var b=this.observed_.length/2-1;this.value_[b]=a.open(this.deliver,this)}},startReset:function(){if(this.state_!=fb)throw Error("Can only reset while open");this.state_=hb,this.disconnect_()},finishReset:function(){if(this.state_!=hb)throw Error("Can only finishReset after startReset");return this.state_=fb,this.connect_(),this.value_},iterateObjects_:function(a){for(var b,c=0;c<this.observed_.length;c+=2)b=this.observed_[c],b!==mb&&this.observed_[c+1].iterateObjects(b,a)},check_:function(a,b){for(var c,d=0;d<this.observed_.length;d+=2){var e,f=this.observed_[d],h=this.observed_[d+1];if(f===mb){var i=h;e=this.state_===eb?i.open(this.deliver,this):i.discardChanges()}else e=h.getValueFrom(f);b?this.value_[d/2]=e:g(e,this.value_[d/2])||(c=c||[],c[d/2]=this.value_[d/2],this.value_[d/2]=e)}return c?(this.report_([this.value_,c,this.observed_]),!0):!1}}),F.prototype={open:function(a,b){return this.callback_=a,this.target_=b,this.value_=this.getValueFn_(this.observable_.open(this.observedCallback_,this)),this.value_},observedCallback_:function(a){if(a=this.getValueFn_(a),!g(a,this.value_)){var b=this.value_;this.value_=a,this.callback_.call(this.target_,this.value_,b)}},discardChanges:function(){return this.value_=this.getValueFn_(this.observable_.discardChanges()),this.value_},deliver:function(){return this.observable_.deliver()},setValue:function(a){return a=this.setValueFn_(a),!this.dontPassThroughSet_&&this.observable_.setValue?this.observable_.setValue(a):void 0},close:function(){this.observable_&&this.observable_.close(),this.callback_=void 0,this.target_=void 0,this.observable_=void 0,this.value_=void 0,this.getValueFn_=void 0,this.setValueFn_=void 0}};var nb={add:!0,update:!0,"delete":!0},ob=0,pb=1,qb=2,rb=3;I.prototype={calcEditDistances:function(a,b,c,d,e,f){for(var g=f-e+1,h=c-b+1,i=new Array(g),j=0;g>j;j++)i[j]=new Array(h),i[j][0]=j;for(var k=0;h>k;k++)i[0][k]=k;for(var j=1;g>j;j++)for(var k=1;h>k;k++)if(this.equals(a[b+k-1],d[e+j-1]))i[j][k]=i[j-1][k-1];else{var l=i[j-1][k]+1,m=i[j][k-1]+1;i[j][k]=m>l?l:m}return i},spliceOperationsFromEditDistances:function(a){for(var b=a.length-1,c=a[0].length-1,d=a[b][c],e=[];b>0||c>0;)if(0!=b)if(0!=c){var f,g=a[b-1][c-1],h=a[b-1][c],i=a[b][c-1];f=i>h?g>h?h:g:g>i?i:g,f==g?(g==d?e.push(ob):(e.push(pb),d=g),b--,c--):f==h?(e.push(rb),b--,d=h):(e.push(qb),c--,d=i)}else e.push(rb),b--;else e.push(qb),c--;return e.reverse(),e},calcSplices:function(a,b,c,d,e,f){var g=0,h=0,i=Math.min(c-b,f-e);if(0==b&&0==e&&(g=this.sharedPrefix(a,d,i)),c==a.length&&f==d.length&&(h=this.sharedSuffix(a,d,i-g)),b+=g,e+=g,c-=h,f-=h,c-b==0&&f-e==0)return[];if(b==c){for(var j=H(b,[],0);f>e;)j.removed.push(d[e++]);return[j]}if(e==f)return[H(b,[],c-b)];for(var k=this.spliceOperationsFromEditDistances(this.calcEditDistances(a,b,c,d,e,f)),j=void 0,l=[],m=b,n=e,o=0;o<k.length;o++)switch(k[o]){case ob:j&&(l.push(j),j=void 0),m++,n++;break;case pb:j||(j=H(m,[],0)),j.addedCount++,m++,j.removed.push(d[n]),n++;break;case qb:j||(j=H(m,[],0)),j.addedCount++,m++;break;case rb:j||(j=H(m,[],0)),j.removed.push(d[n]),n++}return j&&l.push(j),l},sharedPrefix:function(a,b,c){for(var d=0;c>d;d++)if(!this.equals(a[d],b[d]))return d;return c},sharedSuffix:function(a,b,c){for(var d=a.length,e=b.length,f=0;c>f&&this.equals(a[--d],b[--e]);)f++;return f},calculateSplices:function(a,b){return this.calcSplices(a,0,a.length,b,0,b.length)},equals:function(a,b){return a===b}};var sb=new I;a.Observer=x,a.Observer.runEOM_=bb,a.Observer.observerSentinel_=mb,a.Observer.hasObjectObserve=P,a.ArrayObserver=B,a.ArrayObserver.calculateSplices=function(a,b){return sb.calculateSplices(a,b)},a.ArraySplice=I,a.ObjectObserver=A,a.PathObserver=C,a.CompoundObserver=D,a.Path=l,a.ObserverTransform=F}("undefined"!=typeof global&&global&&"undefined"!=typeof module&&module?global:this||window),function(){"use strict";function a(a){for(;a.parentNode;)a=a.parentNode;return"function"==typeof a.getElementById?a:null}function b(a,b,c){var d=a.bindings_;return d||(d=a.bindings_={}),d[b]&&c[b].close(),d[b]=c}function c(a,b,c){return c}function d(a){return null==a?"":a}function e(a,b){a.data=d(b)}function f(a){return function(b){return e(a,b)}}function g(a,b,c,e){return c?void(e?a.setAttribute(b,""):a.removeAttribute(b)):void a.setAttribute(b,d(e))}function h(a,b,c){return function(d){g(a,b,c,d)}}function i(a){switch(a.type){case"checkbox":return u;case"radio":case"select-multiple":case"select-one":return"change";case"range":if(/Trident|MSIE/.test(navigator.userAgent))return"change";default:return"input"}}function j(a,b,c,e){a[b]=(e||d)(c)}function k(a,b,c){return function(d){return j(a,b,d,c)}}function l(){}function m(a,b,c,d){function e(){c.setValue(a[b]),c.discardChanges(),(d||l)(a),Platform.performMicrotaskCheckpoint()}var f=i(a);return a.addEventListener(f,e),{close:function(){a.removeEventListener(f,e),c.close()},observable_:c}}function n(a){return Boolean(a)}function o(b){if(b.form)return s(b.form.elements,function(a){return a!=b&&"INPUT"==a.tagName&&"radio"==a.type&&a.name==b.name});var c=a(b);if(!c)return[];var d=c.querySelectorAll('input[type="radio"][name="'+b.name+'"]');return s(d,function(a){return a!=b&&!a.form})}function p(a){"INPUT"===a.tagName&&"radio"===a.type&&o(a).forEach(function(a){var b=a.bindings_.checked;b&&b.observable_.setValue(!1)})}function q(a,b){var c,e,f,g=a.parentNode;g instanceof HTMLSelectElement&&g.bindings_&&g.bindings_.value&&(c=g,e=c.bindings_.value,f=c.value),a.value=d(b),c&&c.value!=f&&(e.observable_.setValue(c.value),e.observable_.discardChanges(),Platform.performMicrotaskCheckpoint())}function r(a){return function(b){q(a,b)}}var s=Array.prototype.filter.call.bind(Array.prototype.filter);Node.prototype.bind=function(a,b){console.error("Unhandled binding to Node: ",this,a,b)},Node.prototype.bindFinished=function(){};var t=c;Object.defineProperty(Platform,"enableBindingsReflection",{get:function(){return t===b},set:function(a){return t=a?b:c,a},configurable:!0}),Text.prototype.bind=function(a,b,c){if("textContent"!==a)return Node.prototype.bind.call(this,a,b,c);if(c)return e(this,b);var d=b;return e(this,d.open(f(this))),t(this,a,d)},Element.prototype.bind=function(a,b,c){var d="?"==a[a.length-1];if(d&&(this.removeAttribute(a),a=a.slice(0,-1)),c)return g(this,a,d,b);var e=b;return g(this,a,d,e.open(h(this,a,d))),t(this,a,e)};var u;!function(){var a=document.createElement("div"),b=a.appendChild(document.createElement("input"));b.setAttribute("type","checkbox");var c,d=0;b.addEventListener("click",function(){d++,c=c||"click"}),b.addEventListener("change",function(){d++,c=c||"change"});var e=document.createEvent("MouseEvent");e.initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),b.dispatchEvent(e),u=1==d?"change":c}(),HTMLInputElement.prototype.bind=function(a,c,e){if("value"!==a&&"checked"!==a)return HTMLElement.prototype.bind.call(this,a,c,e);this.removeAttribute(a);var f="checked"==a?n:d,g="checked"==a?p:l;if(e)return j(this,a,c,f);var h=c,i=m(this,a,h,g);return j(this,a,h.open(k(this,a,f)),f),b(this,a,i)},HTMLTextAreaElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return j(this,"value",b);var e=b,f=m(this,"value",e);return j(this,"value",e.open(k(this,"value",d))),t(this,a,f)},HTMLOptionElement.prototype.bind=function(a,b,c){if("value"!==a)return HTMLElement.prototype.bind.call(this,a,b,c);if(this.removeAttribute("value"),c)return q(this,b);var d=b,e=m(this,"value",d);return q(this,d.open(r(this))),t(this,a,e)},HTMLSelectElement.prototype.bind=function(a,c,d){if("selectedindex"===a&&(a="selectedIndex"),"selectedIndex"!==a&&"value"!==a)return HTMLElement.prototype.bind.call(this,a,c,d);if(this.removeAttribute(a),d)return j(this,a,c);var e=c,f=m(this,a,e);return j(this,a,e.open(k(this,a))),b(this,a,f)}}(this),function(a){"use strict";function b(a){if(!a)throw new Error("Assertion failed")}function c(a){for(var b;b=a.parentNode;)a=b;return a}function d(a,b){if(b){for(var d,e="#"+b;!d&&(a=c(a),a.protoContent_?d=a.protoContent_.querySelector(e):a.getElementById&&(d=a.getElementById(b)),!d&&a.templateCreator_);)a=a.templateCreator_;return d}}function e(a){return"template"==a.tagName&&"http://www.w3.org/2000/svg"==a.namespaceURI}function f(a){return"TEMPLATE"==a.tagName&&"http://www.w3.org/1999/xhtml"==a.namespaceURI}function g(a){return Boolean(L[a.tagName]&&a.hasAttribute("template"))}function h(a){return void 0===a.isTemplate_&&(a.isTemplate_="TEMPLATE"==a.tagName||g(a)),a.isTemplate_}function i(a,b){var c=a.querySelectorAll(N);h(a)&&b(a),G(c,b)}function j(a){function b(a){HTMLTemplateElement.decorate(a)||j(a.content)}i(a,b)}function k(a,b){Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))})}function l(a){var b=a.ownerDocument;if(!b.defaultView)return b;var c=b.templateContentsOwner_;if(!c){for(c=b.implementation.createHTMLDocument("");c.lastChild;)c.removeChild(c.lastChild);b.templateContentsOwner_=c}return c}function m(a){if(!a.stagingDocument_){var b=a.ownerDocument;if(!b.stagingDocument_){b.stagingDocument_=b.implementation.createHTMLDocument(""),b.stagingDocument_.isStagingDocument=!0;var c=b.stagingDocument_.createElement("base");c.href=document.baseURI,b.stagingDocument_.head.appendChild(c),b.stagingDocument_.stagingDocument_=b.stagingDocument_}a.stagingDocument_=b.stagingDocument_}return a.stagingDocument_}function n(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];K[e.name]&&("template"!==e.name&&b.setAttribute(e.name,e.value),a.removeAttribute(e.name))}return b}function o(a){var b=a.ownerDocument.createElement("template");a.parentNode.insertBefore(b,a);for(var c=a.attributes,d=c.length;d-->0;){var e=c[d];b.setAttribute(e.name,e.value),a.removeAttribute(e.name)}return a.parentNode.removeChild(a),b}function p(a,b,c){var d=a.content;if(c)return void d.appendChild(b);for(var e;e=b.firstChild;)d.appendChild(e)}function q(a){P?a.__proto__=HTMLTemplateElement.prototype:k(a,HTMLTemplateElement.prototype)}function r(a){a.setModelFn_||(a.setModelFn_=function(){a.setModelFnScheduled_=!1;
|
| -var b=z(a,a.delegate_&&a.delegate_.prepareBinding);w(a,b,a.model_)}),a.setModelFnScheduled_||(a.setModelFnScheduled_=!0,Observer.runEOM_(a.setModelFn_))}function s(a,b,c,d){if(a&&a.length){for(var e,f=a.length,g=0,h=0,i=0,j=!0;f>h;){var g=a.indexOf("{{",h),k=a.indexOf("[[",h),l=!1,m="}}";if(k>=0&&(0>g||g>k)&&(g=k,l=!0,m="]]"),i=0>g?-1:a.indexOf(m,g+2),0>i){if(!e)return;e.push(a.slice(h));break}e=e||[],e.push(a.slice(h,g));var n=a.slice(g+2,i).trim();e.push(l),j=j&&l;var o=d&&d(n,b,c);e.push(null==o?Path.get(n):null),e.push(o),h=i+2}return h===f&&e.push(""),e.hasOnePath=5===e.length,e.isSimplePath=e.hasOnePath&&""==e[0]&&""==e[4],e.onlyOneTime=j,e.combinator=function(a){for(var b=e[0],c=1;c<e.length;c+=4){var d=e.hasOnePath?a:a[(c-1)/4];void 0!==d&&(b+=d),b+=e[c+3]}return b},e}}function t(a,b,c,d){if(b.hasOnePath){var e=b[3],f=e?e(d,c,!0):b[2].getValueFrom(d);return b.isSimplePath?f:b.combinator(f)}for(var g=[],h=1;h<b.length;h+=4){var e=b[h+2];g[(h-1)/4]=e?e(d,c):b[h+1].getValueFrom(d)}return b.combinator(g)}function u(a,b,c,d){var e=b[3],f=e?e(d,c,!1):new PathObserver(d,b[2]);return b.isSimplePath?f:new ObserverTransform(f,b.combinator)}function v(a,b,c,d){if(b.onlyOneTime)return t(a,b,c,d);if(b.hasOnePath)return u(a,b,c,d);for(var e=new CompoundObserver,f=1;f<b.length;f+=4){var g=b[f],h=b[f+2];if(h){var i=h(d,c,g);g?e.addPath(i):e.addObserver(i)}else{var j=b[f+1];g?e.addPath(j.getValueFrom(d)):e.addPath(d,j)}}return new ObserverTransform(e,b.combinator)}function w(a,b,c,d){for(var e=0;e<b.length;e+=2){var f=b[e],g=b[e+1],h=v(f,g,a,c),i=a.bind(f,h,g.onlyOneTime);i&&d&&d.push(i)}if(a.bindFinished(),b.isTemplate){a.model_=c;var j=a.processBindingDirectives_(b);d&&j&&d.push(j)}}function x(a,b,c){var d=a.getAttribute(b);return s(""==d?"{{}}":d,b,a,c)}function y(a,c){b(a);for(var d=[],e=0;e<a.attributes.length;e++){for(var f=a.attributes[e],g=f.name,i=f.value;"_"===g[0];)g=g.substring(1);if(!h(a)||g!==J&&g!==H&&g!==I){var j=s(i,g,a,c);j&&d.push(g,j)}}return h(a)&&(d.isTemplate=!0,d.if=x(a,J,c),d.bind=x(a,H,c),d.repeat=x(a,I,c),!d.if||d.bind||d.repeat||(d.bind=s("{{}}",H,a,c))),d}function z(a,b){if(a.nodeType===Node.ELEMENT_NODE)return y(a,b);if(a.nodeType===Node.TEXT_NODE){var c=s(a.data,"textContent",a,b);if(c)return["textContent",c]}return[]}function A(a,b,c,d,e,f,g){for(var h=b.appendChild(c.importNode(a,!1)),i=0,j=a.firstChild;j;j=j.nextSibling)A(j,h,c,d.children[i++],e,f,g);return d.isTemplate&&(HTMLTemplateElement.decorate(h,a),f&&h.setDelegate_(f)),w(h,d,e,g),h}function B(a,b){var c=z(a,b);c.children={};for(var d=0,e=a.firstChild;e;e=e.nextSibling)c.children[d++]=B(e,b);return c}function C(a){var b=a.id_;return b||(b=a.id_=S++),b}function D(a,b){var c=C(a);if(b){var d=b.bindingMaps[c];return d||(d=b.bindingMaps[c]=B(a,b.prepareBinding)||[]),d}var d=a.bindingMap_;return d||(d=a.bindingMap_=B(a,void 0)||[]),d}function E(a){this.closed=!1,this.templateElement_=a,this.instances=[],this.deps=void 0,this.iteratedValue=[],this.presentValue=void 0,this.arrayObserver=void 0}var F,G=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.Map&&"function"==typeof a.Map.prototype.forEach?F=a.Map:(F=function(){this.keys=[],this.values=[]},F.prototype={set:function(a,b){var c=this.keys.indexOf(a);0>c?(this.keys.push(a),this.values.push(b)):this.values[c]=b},get:function(a){var b=this.keys.indexOf(a);if(!(0>b))return this.values[b]},"delete":function(a){var b=this.keys.indexOf(a);return 0>b?!1:(this.keys.splice(b,1),this.values.splice(b,1),!0)},forEach:function(a,b){for(var c=0;c<this.keys.length;c++)a.call(b||this,this.values[c],this.keys[c],this)}});"function"!=typeof document.contains&&(Document.prototype.contains=function(a){return a===this||a.parentNode===this?!0:this.documentElement.contains(a)});var H="bind",I="repeat",J="if",K={template:!0,repeat:!0,bind:!0,ref:!0},L={THEAD:!0,TBODY:!0,TFOOT:!0,TH:!0,TR:!0,TD:!0,COLGROUP:!0,COL:!0,CAPTION:!0,OPTION:!0,OPTGROUP:!0},M="undefined"!=typeof HTMLTemplateElement;M&&!function(){var a=document.createElement("template"),b=a.content.ownerDocument,c=b.appendChild(b.createElement("html")),d=c.appendChild(b.createElement("head")),e=b.createElement("base");e.href=document.baseURI,d.appendChild(e)}();var N="template, "+Object.keys(L).map(function(a){return a.toLowerCase()+"[template]"}).join(", ");document.addEventListener("DOMContentLoaded",function(){j(document),Platform.performMicrotaskCheckpoint()},!1),M||(a.HTMLTemplateElement=function(){throw TypeError("Illegal constructor")});var O,P="__proto__"in{};"function"==typeof MutationObserver&&(O=new MutationObserver(function(a){for(var b=0;b<a.length;b++)a[b].target.refChanged_()})),HTMLTemplateElement.decorate=function(a,c){if(a.templateIsDecorated_)return!1;var d=a;d.templateIsDecorated_=!0;var h=f(d)&&M,i=h,k=!h,m=!1;if(h||(g(d)?(b(!c),d=n(a),d.templateIsDecorated_=!0,h=M,m=!0):e(d)&&(d=o(a),d.templateIsDecorated_=!0,h=M)),!h){q(d);var r=l(d);d.content_=r.createDocumentFragment()}return c?d.instanceRef_=c:k?p(d,a,m):i&&j(d.content),!0},HTMLTemplateElement.bootstrap=j;var Q=a.HTMLUnknownElement||HTMLElement,R={get:function(){return this.content_},enumerable:!0,configurable:!0};M||(HTMLTemplateElement.prototype=Object.create(Q.prototype),Object.defineProperty(HTMLTemplateElement.prototype,"content",R)),k(HTMLTemplateElement.prototype,{bind:function(a,b,c){if("ref"!=a)return Element.prototype.bind.call(this,a,b,c);var d=this,e=c?b:b.open(function(a){d.setAttribute("ref",a),d.refChanged_()});return this.setAttribute("ref",e),this.refChanged_(),c?void 0:(this.bindings_?this.bindings_.ref=b:this.bindings_={ref:b},b)},processBindingDirectives_:function(a){return this.iterator_&&this.iterator_.closeDeps(),a.if||a.bind||a.repeat?(this.iterator_||(this.iterator_=new E(this)),this.iterator_.updateDependencies(a,this.model_),O&&O.observe(this,{attributes:!0,attributeFilter:["ref"]}),this.iterator_):void(this.iterator_&&(this.iterator_.close(),this.iterator_=void 0))},createInstance:function(a,b,c){b?c=this.newDelegate_(b):c||(c=this.delegate_),this.refContent_||(this.refContent_=this.ref_.content);var d=this.refContent_;if(null===d.firstChild)return T;var e=D(d,c),f=m(this),g=f.createDocumentFragment();g.templateCreator_=this,g.protoContent_=d,g.bindings_=[],g.terminator_=null;for(var h=g.templateInstance_={firstNode:null,lastNode:null,model:a},i=0,j=!1,k=d.firstChild;k;k=k.nextSibling){null===k.nextSibling&&(j=!0);var l=A(k,g,f,e.children[i++],a,c,g.bindings_);l.templateInstance_=h,j&&(g.terminator_=l)}return h.firstNode=g.firstChild,h.lastNode=g.lastChild,g.templateCreator_=void 0,g.protoContent_=void 0,g},get model(){return this.model_},set model(a){this.model_=a,r(this)},get bindingDelegate(){return this.delegate_&&this.delegate_.raw},refChanged_:function(){this.iterator_&&this.refContent_!==this.ref_.content&&(this.refContent_=void 0,this.iterator_.valueChanged(),this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue()))},clear:function(){this.model_=void 0,this.delegate_=void 0,this.bindings_&&this.bindings_.ref&&this.bindings_.ref.close(),this.refContent_=void 0,this.iterator_&&(this.iterator_.valueChanged(),this.iterator_.close(),this.iterator_=void 0)},setDelegate_:function(a){this.delegate_=a,this.bindingMap_=void 0,this.iterator_&&(this.iterator_.instancePositionChangedFn_=void 0,this.iterator_.instanceModelFn_=void 0)},newDelegate_:function(a){function b(b){var c=a&&a[b];if("function"==typeof c)return function(){return c.apply(a,arguments)}}if(a)return{bindingMaps:{},raw:a,prepareBinding:b("prepareBinding"),prepareInstanceModel:b("prepareInstanceModel"),prepareInstancePositionChanged:b("prepareInstancePositionChanged")}},set bindingDelegate(a){if(this.delegate_)throw Error("Template must be cleared before a new bindingDelegate can be assigned");this.setDelegate_(this.newDelegate_(a))},get ref_(){var a=d(this,this.getAttribute("ref"));if(a||(a=this.instanceRef_),!a)return this;var b=a.ref_;return b?b:a}});var S=1;Object.defineProperty(Node.prototype,"templateInstance",{get:function(){var a=this.templateInstance_;return a?a:this.parentNode?this.parentNode.templateInstance:void 0}});var T=document.createDocumentFragment();T.bindings_=[],T.terminator_=null,E.prototype={closeDeps:function(){var a=this.deps;a&&(a.ifOneTime===!1&&a.ifValue.close(),a.oneTime===!1&&a.value.close())},updateDependencies:function(a,b){this.closeDeps();var c=this.deps={},d=this.templateElement_,e=!0;if(a.if){if(c.hasIf=!0,c.ifOneTime=a.if.onlyOneTime,c.ifValue=v(J,a.if,d,b),e=c.ifValue,c.ifOneTime&&!e)return void this.valueChanged();c.ifOneTime||(e=e.open(this.updateIfValue,this))}a.repeat?(c.repeat=!0,c.oneTime=a.repeat.onlyOneTime,c.value=v(I,a.repeat,d,b)):(c.repeat=!1,c.oneTime=a.bind.onlyOneTime,c.value=v(H,a.bind,d,b));var f=c.value;return c.oneTime||(f=f.open(this.updateIteratedValue,this)),e?void this.updateValue(f):void this.valueChanged()},getUpdatedValue:function(){var a=this.deps.value;return this.deps.oneTime||(a=a.discardChanges()),a},updateIfValue:function(a){return a?void this.updateValue(this.getUpdatedValue()):void this.valueChanged()},updateIteratedValue:function(a){if(this.deps.hasIf){var b=this.deps.ifValue;if(this.deps.ifOneTime||(b=b.discardChanges()),!b)return void this.valueChanged()}this.updateValue(a)},updateValue:function(a){this.deps.repeat||(a=[a]);var b=this.deps.repeat&&!this.deps.oneTime&&Array.isArray(a);this.valueChanged(a,b)},valueChanged:function(a,b){Array.isArray(a)||(a=[]),a!==this.iteratedValue&&(this.unobserve(),this.presentValue=a,b&&(this.arrayObserver=new ArrayObserver(this.presentValue),this.arrayObserver.open(this.handleSplices,this)),this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,this.iteratedValue)))},getLastInstanceNode:function(a){if(-1==a)return this.templateElement_;var b=this.instances[a],c=b.terminator_;if(!c)return this.getLastInstanceNode(a-1);if(c.nodeType!==Node.ELEMENT_NODE||this.templateElement_===c)return c;var d=c.iterator_;return d?d.getLastTemplateNode():c},getLastTemplateNode:function(){return this.getLastInstanceNode(this.instances.length-1)},insertInstanceAt:function(a,b){var c=this.getLastInstanceNode(a-1),d=this.templateElement_.parentNode;this.instances.splice(a,0,b),d.insertBefore(b,c.nextSibling)},extractInstanceAt:function(a){for(var b=this.getLastInstanceNode(a-1),c=this.getLastInstanceNode(a),d=this.templateElement_.parentNode,e=this.instances.splice(a,1)[0];c!==b;){var f=b.nextSibling;f==c&&(c=b),e.appendChild(d.removeChild(f))}return e},getDelegateFn:function(a){return a=a&&a(this.templateElement_),"function"==typeof a?a:null},handleSplices:function(a){if(!this.closed&&a.length){var b=this.templateElement_;if(!b.parentNode)return void this.close();ArrayObserver.applySplices(this.iteratedValue,this.presentValue,a);var c=b.delegate_;void 0===this.instanceModelFn_&&(this.instanceModelFn_=this.getDelegateFn(c&&c.prepareInstanceModel)),void 0===this.instancePositionChangedFn_&&(this.instancePositionChangedFn_=this.getDelegateFn(c&&c.prepareInstancePositionChanged));for(var d=new F,e=0,f=0;f<a.length;f++){for(var g=a[f],h=g.removed,i=0;i<h.length;i++){var j=h[i],k=this.extractInstanceAt(g.index+e);k!==T&&d.set(j,k)}e-=g.addedCount}for(var f=0;f<a.length;f++)for(var g=a[f],l=g.index;l<g.index+g.addedCount;l++){var j=this.iteratedValue[l],k=d.get(j);k?d.delete(j):(this.instanceModelFn_&&(j=this.instanceModelFn_(j)),k=void 0===j?T:b.createInstance(j,void 0,c)),this.insertInstanceAt(l,k)}d.forEach(function(a){this.closeInstanceBindings(a)},this),this.instancePositionChangedFn_&&this.reportInstancesMoved(a)}},reportInstanceMoved:function(a){var b=this.instances[a];b!==T&&this.instancePositionChangedFn_(b.templateInstance_,a)},reportInstancesMoved:function(a){for(var b=0,c=0,d=0;d<a.length;d++){var e=a[d];if(0!=c)for(;b<e.index;)this.reportInstanceMoved(b),b++;else b=e.index;for(;b<e.index+e.addedCount;)this.reportInstanceMoved(b),b++;c+=e.addedCount-e.removed.length}if(0!=c)for(var f=this.instances.length;f>b;)this.reportInstanceMoved(b),b++},closeInstanceBindings:function(a){for(var b=a.bindings_,c=0;c<b.length;c++)b[c].close()},unobserve:function(){this.arrayObserver&&(this.arrayObserver.close(),this.arrayObserver=void 0)},close:function(){if(!this.closed){this.unobserve();for(var a=0;a<this.instances.length;a++)this.closeInstanceBindings(this.instances[a]);this.instances.length=0,this.closeDeps(),this.templateElement_.iterator_=void 0,this.closed=!0}}},HTMLTemplateElement.forAllTemplatesFrom_=i}(this),function(a){function b(a){f.textContent=d++,e.push(a)}function c(){for(;e.length;)e.shift()()}var d=0,e=[],f=document.createTextNode("");new(window.MutationObserver||JsMutationObserver)(c).observe(f,{characterData:!0}),a.endOfMicrotask=b}(Platform),function(a){function b(){e||(e=!0,a.endOfMicrotask(function(){e=!1,logFlags.data&&console.group("Platform.flush()"),a.performMicrotaskCheckpoint(),logFlags.data&&console.groupEnd()}))}var c=document.createElement("style");c.textContent="template {display: none !important;} /* injected by platform.js */";var d=document.querySelector("head");d.insertBefore(c,d.firstChild);var e;if(Observer.hasObjectObserve)b=function(){};else{var f=125;window.addEventListener("WebComponentsReady",function(){b(),a.flushPoll=setInterval(b,f)})}if(window.CustomElements&&!CustomElements.useNative){var g=Document.prototype.importNode;Document.prototype.importNode=function(a,b){var c=g.call(this,a,b);return CustomElements.upgradeAll(c),c}}a.flush=b}(window.Platform),function(a){function b(a,b,d,e){return a.replace(e,function(a,e,f,g){var h=f.replace(/["']/g,"");return h=c(b,h,d),e+"'"+h+"'"+g})}function c(a,b,c){if(b&&"/"===b[0])return b;var e=new URL(b,a);return c?e.href:d(e.href)}function d(a){var b=new URL(document.baseURI),c=new URL(a,b);return c.host===b.host&&c.port===b.port&&c.protocol===b.protocol?e(b,c):a}function e(a,b){for(var c=a.pathname,d=b.pathname,e=c.split("/"),f=d.split("/");e.length&&e[0]===f[0];)e.shift(),f.shift();for(var g=0,h=e.length-1;h>g;g++)f.unshift("..");return f.join("/")+b.search+b.hash}var f={resolveDom:function(a,b){b=b||a.ownerDocument.baseURI,this.resolveAttributes(a,b),this.resolveStyles(a,b);var c=a.querySelectorAll("template");if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)d.content&&this.resolveDom(d.content,b)},resolveTemplate:function(a){this.resolveDom(a.content,a.ownerDocument.baseURI)},resolveStyles:function(a,b){var c=a.querySelectorAll("style");if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveStyle(d,b)},resolveStyle:function(a,b){b=b||a.ownerDocument.baseURI,a.textContent=this.resolveCssText(a.textContent,b)},resolveCssText:function(a,c,d){return a=b(a,c,d,g),b(a,c,d,h)},resolveAttributes:function(a,b){a.hasAttributes&&a.hasAttributes()&&this.resolveElementAttributes(a,b);var c=a&&a.querySelectorAll(j);if(c)for(var d,e=0,f=c.length;f>e&&(d=c[e]);e++)this.resolveElementAttributes(d,b)},resolveElementAttributes:function(a,d){d=d||a.ownerDocument.baseURI,i.forEach(function(e){var f,h=a.attributes[e],i=h&&h.value;i&&i.search(k)<0&&(f="style"===e?b(i,d,!1,g):c(d,i),h.value=f)})}},g=/(url\()([^)]*)(\))/g,h=/(@import[\s]+(?!url\())([^;]*)(;)/g,i=["href","src","action","style","url"],j="["+i.join("],[")+"]",k="{{.*}}";a.urlResolver=f}(Polymer),function(a){function b(a){this.cache=Object.create(null),this.map=Object.create(null),this.requests=0,this.regex=a}var c=Platform.endOfMicrotask;b.prototype={extractUrls:function(a,b){for(var c,d,e=[];c=this.regex.exec(a);)d=new URL(c[1],b),e.push({matched:c[0],url:d.href});return e},process:function(a,b,c){var d=this.extractUrls(a,b),e=c.bind(null,this.map);this.fetch(d,e)},fetch:function(a,b){var c=a.length;if(!c)return b();for(var d,e,f,g=function(){0===--c&&b()},h=0;c>h;h++)d=a[h],f=d.url,e=this.cache[f],e||(e=this.xhr(f),e.match=d,this.cache[f]=e),e.wait(g)},handleXhr:function(a){var b=a.match,c=b.url,d=a.response||a.responseText||"";this.map[c]=d,this.fetch(this.extractUrls(d,c),a.resolve)},xhr:function(a){this.requests++;var b=new XMLHttpRequest;return b.open("GET",a,!0),b.send(),b.onerror=b.onload=this.handleXhr.bind(this,b),b.pending=[],b.resolve=function(){for(var a=b.pending,c=0;c<a.length;c++)a[c]();b.pending=null},b.wait=function(a){b.pending?b.pending.push(a):c(a)},b}},a.Loader=b}(Polymer),function(a){function b(){this.loader=new d(this.regex)}var c=a.urlResolver,d=a.Loader;b.prototype={regex:/@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,resolve:function(a,b,c){var d=function(d){c(this.flatten(a,b,d))}.bind(this);this.loader.process(a,b,d)},resolveNode:function(a,b,c){var d=a.textContent,e=function(b){a.textContent=b,c(a)};this.resolve(d,b,e)},flatten:function(a,b,d){for(var e,f,g,h=this.loader.extractUrls(a,b),i=0;i<h.length;i++)e=h[i],f=e.url,g=c.resolveCssText(d[f],f,!0),g=this.flatten(g,b,d),a=a.replace(e.matched,g);return a},loadStyles:function(a,b,c){function d(){f++,f===g&&c&&c()}for(var e,f=0,g=a.length,h=0;g>h&&(e=a[h]);h++)this.resolveNode(e,b,d)}};var e=new b;a.styleResolver=e}(Polymer),function(a){function b(a,b){return a&&b&&Object.getOwnPropertyNames(b).forEach(function(c){var d=Object.getOwnPropertyDescriptor(b,c);d&&(Object.defineProperty(a,c,d),"function"==typeof d.value&&(d.value.nom=c))}),a}function c(a){for(var b=a||{},c=1;c<arguments.length;c++){var e=arguments[c];try{for(var f in e)d(f,e,b)}catch(g){}}return b}function d(a,b,c){var d=e(b,a);Object.defineProperty(c,a,d)}function e(a,b){if(a){var c=Object.getOwnPropertyDescriptor(a,b);return c||e(Object.getPrototypeOf(a),b)}}a.extend=b,a.mixin=c,Platform.mixin=c}(Polymer),function(a){function b(a,b,d){return a?a.stop():a=new c(this),a.go(b,d),a}var c=function(a){this.context=a,this.boundComplete=this.complete.bind(this)};c.prototype={go:function(a,b){this.callback=a;var c;b?(c=setTimeout(this.boundComplete,b),this.handle=function(){clearTimeout(c)}):(c=requestAnimationFrame(this.boundComplete),this.handle=function(){cancelAnimationFrame(c)})},stop:function(){this.handle&&(this.handle(),this.handle=null)},complete:function(){this.handle&&(this.stop(),this.callback.call(this.context))}},a.job=b}(Polymer),function(a){function b(a,b,c){var d="string"==typeof a?document.createElement(a):a.cloneNode(!0);if(d.innerHTML=b,c)for(var e in c)d.setAttribute(e,c[e]);return d}var c={};HTMLElement.register=function(a,b){c[a]=b},HTMLElement.getPrototypeForTag=function(a){var b=a?c[a]:HTMLElement.prototype;return b||Object.getPrototypeOf(document.createElement(a))};var d=Event.prototype.stopPropagation;Event.prototype.stopPropagation=function(){this.cancelBubble=!0,d.apply(this,arguments)};var e=DOMTokenList.prototype.add,f=DOMTokenList.prototype.remove;DOMTokenList.prototype.add=function(){for(var a=0;a<arguments.length;a++)e.call(this,arguments[a])},DOMTokenList.prototype.remove=function(){for(var a=0;a<arguments.length;a++)f.call(this,arguments[a])},DOMTokenList.prototype.toggle=function(a,b){1==arguments.length&&(b=!this.contains(a)),b?this.add(a):this.remove(a)},DOMTokenList.prototype.switch=function(a,b){a&&this.remove(a),b&&this.add(b)};var g=function(){return Array.prototype.slice.call(this)},h=window.NamedNodeMap||window.MozNamedAttrMap||{};NodeList.prototype.array=g,h.prototype.array=g,HTMLCollection.prototype.array=g,a.createDOM=b}(Polymer),function(a){function b(a){var e=b.caller,g=e.nom,h=e._super;h||(g||(g=e.nom=c.call(this,e)),g||console.warn("called super() on a method not installed declaratively (has no .nom property)"),h=d(e,g,f(this)));var i=h[g];return i?(i._super||d(i,g,h),i.apply(this,a||[])):void 0}function c(a){for(var b=this.__proto__;b&&b!==HTMLElement.prototype;){for(var c,d=Object.getOwnPropertyNames(b),e=0,f=d.length;f>e&&(c=d[e]);e++){var g=Object.getOwnPropertyDescriptor(b,c);if("function"==typeof g.value&&g.value===a)return c}b=b.__proto__}}function d(a,b,c){var d=e(c,b,a);return d[b]&&(d[b].nom=b),a._super=d}function e(a,b,c){for(;a;){if(a[b]!==c&&a[b])return a;a=f(a)}return Object}function f(a){return a.__proto__}a.super=b}(Polymer),function(a){function b(a){return a}function c(a,b){var c=typeof b;return b instanceof Date&&(c="date"),d[c](a,b)}var d={string:b,undefined:b,date:function(a){return new Date(Date.parse(a)||Date.now())},"boolean":function(a){return""===a?!0:"false"===a?!1:!!a},number:function(a){var b=parseFloat(a);return 0===b&&(b=parseInt(a)),isNaN(b)?a:b},object:function(a,b){if(null===b)return a;try{return JSON.parse(a.replace(/'/g,'"'))}catch(c){return a}},"function":function(a,b){return b}};a.deserializeValue=c}(Polymer),function(a){var b=a.extend,c={};c.declaration={},c.instance={},c.publish=function(a,c){for(var d in a)b(c,a[d])},a.api=c}(Polymer),function(a){var b={async:function(a,b,c){Platform.flush(),b=b&&b.length?b:[b];var d=function(){(this[a]||a).apply(this,b)}.bind(this),e=c?setTimeout(d,c):requestAnimationFrame(d);return c?e:~e},cancelAsync:function(a){0>a?cancelAnimationFrame(~a):clearTimeout(a)},fire:function(a,b,c,d,e){var f=c||this,b=null===b||void 0===b?{}:b,g=new CustomEvent(a,{bubbles:void 0!==d?d:!0,cancelable:void 0!==e?e:!0,detail:b});return f.dispatchEvent(g),g},asyncFire:function(){this.async("fire",arguments)},classFollows:function(a,b,c){b&&b.classList.remove(c),a&&a.classList.add(c)},injectBoundHTML:function(a,b){var c=document.createElement("template");c.innerHTML=a;var d=this.instanceTemplate(c);return b&&(b.textContent="",b.appendChild(d)),d}},c=function(){},d={};b.asyncMethod=b.async,a.api.instance.utils=b,a.nop=c,a.nob=d}(Polymer),function(a){var b=window.logFlags||{},c="on-",d={EVENT_PREFIX:c,addHostListeners:function(){var a=this.eventDelegates;b.events&&Object.keys(a).length>0&&console.log("[%s] addHostListeners:",this.localName,a);for(var c in a){var d=a[c];PolymerGestures.addEventListener(this,c,this.element.getEventHandler(this,this,d))}},dispatchMethod:function(a,c,d){if(a){b.events&&console.group("[%s] dispatch [%s]",a.localName,c);var e="function"==typeof c?c:a[c];e&&e[d?"apply":"call"](a,d),b.events&&console.groupEnd(),Platform.flush()}}};a.api.instance.events=d,a.addEventListener=function(a,b,c,d){PolymerGestures.addEventListener(wrap(a),b,c,d)},a.removeEventListener=function(a,b,c,d){PolymerGestures.removeEventListener(wrap(a),b,c,d)}}(Polymer),function(a){var b={copyInstanceAttributes:function(){var a=this._instanceAttributes;for(var b in a)this.hasAttribute(b)||this.setAttribute(b,a[b])},takeAttributes:function(){if(this._publishLC)for(var a,b=0,c=this.attributes,d=c.length;(a=c[b])&&d>b;b++)this.attributeToProperty(a.name,a.value)},attributeToProperty:function(b,c){var b=this.propertyForAttribute(b);if(b){if(c&&c.search(a.bindPattern)>=0)return;var d=this[b],c=this.deserializeValue(c,d);c!==d&&(this[b]=c)}},propertyForAttribute:function(a){var b=this._publishLC&&this._publishLC[a];return b},deserializeValue:function(b,c){return a.deserializeValue(b,c)},serializeValue:function(a,b){return"boolean"===b?a?"":void 0:"object"!==b&&"function"!==b&&void 0!==a?a:void 0},reflectPropertyToAttribute:function(a){var b=typeof this[a],c=this.serializeValue(this[a],b);void 0!==c?this.setAttribute(a,c):"boolean"===b&&this.removeAttribute(a)}};a.api.instance.attributes=b}(Polymer),function(a){function b(a,b){return a===b?0!==a||1/a===1/b:f(a)&&f(b)?!0:a!==a&&b!==b}function c(a,b){return void 0===b&&null===a?b:null===b||void 0===b?a:b}var d=window.logFlags||{},e={object:void 0,type:"update",name:void 0,oldValue:void 0},f=Number.isNaN||function(a){return"number"==typeof a&&isNaN(a)},g={createPropertyObserver:function(){var a=this._observeNames;if(a&&a.length){var b=this._propertyObserver=new CompoundObserver(!0);this.registerObserver(b);for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.addPath(this,c),this.observeArrayValue(c,this[c],null)}},openPropertyObserver:function(){this._propertyObserver&&this._propertyObserver.open(this.notifyPropertyChanges,this)},notifyPropertyChanges:function(a,b,c){var d,e,f={};for(var g in b)if(d=c[2*g+1],e=this.observe[d]){var h=b[g],i=a[g];this.observeArrayValue(d,i,h),f[e]||(void 0!==h&&null!==h||void 0!==i&&null!==i)&&(f[e]=!0,this.invokeMethod(e,[h,i,arguments]))}},deliverChanges:function(){this._propertyObserver&&this._propertyObserver.deliver()},propertyChanged_:function(a){this.reflect[a]&&this.reflectPropertyToAttribute(a)},observeArrayValue:function(a,b,c){var e=this.observe[a];if(e&&(Array.isArray(c)&&(d.observe&&console.log("[%s] observeArrayValue: unregister observer [%s]",this.localName,a),this.closeNamedObserver(a+"__array")),Array.isArray(b))){d.observe&&console.log("[%s] observeArrayValue: register observer [%s]",this.localName,a,b);var f=new ArrayObserver(b);f.open(function(a){this.invokeMethod(e,[a])},this),this.registerNamedObserver(a+"__array",f)}},emitPropertyChangeRecord:function(a,c,d){if(!b(c,d)&&(this.propertyChanged_(a,c,d),Observer.hasObjectObserve)){var f=this.notifier_;f||(f=this.notifier_=Object.getNotifier(this)),e.object=this,e.name=a,e.oldValue=d,f.notify(e)}},bindToAccessor:function(a,c,d){function e(b,c){j[f]=b;var d=j[h];d&&"function"==typeof d.setValue&&d.setValue(b),j.emitPropertyChangeRecord(a,b,c)}var f=a+"_",g=a+"Observable_",h=a+"ComputedBoundObservable_";this[g]=c;var i=this[f],j=this,k=c.open(e);if(d&&!b(i,k)){var l=d(i,k);b(k,l)||(k=l,c.setValue&&c.setValue(k))}e(k,i);var m={close:function(){c.close(),j[g]=void 0,j[h]=void 0}};return this.registerObserver(m),m},createComputedProperties:function(){if(this._computedNames)for(var a=0;a<this._computedNames.length;a++){var b=this._computedNames[a],c=this.computed[b];try{var d=PolymerExpressions.getExpression(c),e=d.getBinding(this,this.element.syntax);this.bindToAccessor(b,e)}catch(f){console.error("Failed to create computed property",f)}}},bindProperty:function(a,b,d){if(d)return void(this[a]=b);var e=this.element.prototype.computed;if(e&&e[a]){var f=a+"ComputedBoundObservable_";return void(this[f]=b)}return this.bindToAccessor(a,b,c)},invokeMethod:function(a,b){var c=this[a]||a;"function"==typeof c&&c.apply(this,b)},registerObserver:function(a){return this._observers?void this._observers.push(a):void(this._observers=[a])},closeObservers:function(){if(this._observers){for(var a=this._observers,b=0;b<a.length;b++){var c=a[b];c&&"function"==typeof c.close&&c.close()}this._observers=[]}},registerNamedObserver:function(a,b){var c=this._namedObservers||(this._namedObservers={});c[a]=b},closeNamedObserver:function(a){var b=this._namedObservers;return b&&b[a]?(b[a].close(),b[a]=null,!0):void 0},closeNamedObservers:function(){if(this._namedObservers){for(var a in this._namedObservers)this.closeNamedObserver(a);this._namedObservers={}}}};a.api.instance.properties=g}(Polymer),function(a){var b=window.logFlags||0,c={instanceTemplate:function(a){HTMLTemplateElement.decorate(a);for(var b=this.syntax||!a.bindingDelegate&&this.element.syntax,c=a.createInstance(this,b),d=c.bindings_,e=0;e<d.length;e++)this.registerObserver(d[e]);return c},bind:function(a,b,c){var d=this.propertyForAttribute(a);if(d){var e=this.bindProperty(d,b,c);return Platform.enableBindingsReflection&&e&&(e.path=b.path_,this._recordBinding(d,e)),this.reflect[d]&&this.reflectPropertyToAttribute(d),e}return this.mixinSuper(arguments)},bindFinished:function(){this.makeElementReady()},_recordBinding:function(a,b){this.bindings_=this.bindings_||{},this.bindings_[a]=b},asyncUnbindAll:function(){this._unbound||(b.unbind&&console.log("[%s] asyncUnbindAll",this.localName),this._unbindAllJob=this.job(this._unbindAllJob,this.unbindAll,0))},unbindAll:function(){this._unbound||(this.closeObservers(),this.closeNamedObservers(),this._unbound=!0)},cancelUnbindAll:function(){return this._unbound?void(b.unbind&&console.warn("[%s] already unbound, cannot cancel unbindAll",this.localName)):(b.unbind&&console.log("[%s] cancelUnbindAll",this.localName),void(this._unbindAllJob&&(this._unbindAllJob=this._unbindAllJob.stop())))}},d=/\{\{([^{}]*)}}/;a.bindPattern=d,a.api.instance.mdv=c}(Polymer),function(a){function b(a){return a.hasOwnProperty("PolymerBase")}function c(){}var d={PolymerBase:!0,job:function(a,b,c){if("string"!=typeof a)return Polymer.job.call(this,a,b,c);var d="___"+a;this[d]=Polymer.job.call(this,this[d],b,c)},"super":Polymer.super,created:function(){},ready:function(){},createdCallback:function(){this.templateInstance&&this.templateInstance.model&&console.warn("Attributes on "+this.localName+" were data bound prior to Polymer upgrading the element. This may result in incorrect binding types."),this.created(),this.prepareElement(),this.ownerDocument.isStagingDocument||this.makeElementReady()},prepareElement:function(){return this._elementPrepared?void console.warn("Element already prepared",this.localName):(this._elementPrepared=!0,this.shadowRoots={},this.createPropertyObserver(),this.openPropertyObserver(),this.copyInstanceAttributes(),this.takeAttributes(),void this.addHostListeners())},makeElementReady:function(){this._readied||(this._readied=!0,this.createComputedProperties(),this.parseDeclarations(this.__proto__),this.removeAttribute("unresolved"),this.ready())},attachedCallback:function(){this.cancelUnbindAll(),this.attached&&this.attached(),this.enteredView&&this.enteredView(),this.hasBeenAttached||(this.hasBeenAttached=!0,this.domReady&&this.async("domReady"))},detachedCallback:function(){this.preventDispose||this.asyncUnbindAll(),this.detached&&this.detached(),this.leftView&&this.leftView()},enteredViewCallback:function(){this.attachedCallback()},leftViewCallback:function(){this.detachedCallback()},enteredDocumentCallback:function(){this.attachedCallback()},leftDocumentCallback:function(){this.detachedCallback()},parseDeclarations:function(a){a&&a.element&&(this.parseDeclarations(a.__proto__),a.parseDeclaration.call(this,a.element))},parseDeclaration:function(a){var b=this.fetchTemplate(a);if(b){var c=this.shadowFromTemplate(b);this.shadowRoots[a.name]=c}},fetchTemplate:function(a){return a.querySelector("template")},shadowFromTemplate:function(a){if(a){var b=this.createShadowRoot(),c=this.instanceTemplate(a);return b.appendChild(c),this.shadowRootReady(b,a),b}},lightFromTemplate:function(a,b){if(a){this.eventController=this;var c=this.instanceTemplate(a);return b?this.insertBefore(c,b):this.appendChild(c),this.shadowRootReady(this),c}},shadowRootReady:function(a){this.marshalNodeReferences(a)},marshalNodeReferences:function(a){var b=this.$=this.$||{};if(a)for(var c,d=a.querySelectorAll("[id]"),e=0,f=d.length;f>e&&(c=d[e]);e++)b[c.id]=c},attributeChangedCallback:function(a){"class"!==a&&"style"!==a&&this.attributeToProperty(a,this.getAttribute(a)),this.attributeChanged&&this.attributeChanged.apply(this,arguments)},onMutation:function(a,b){var c=new MutationObserver(function(a){b.call(this,c,a),c.disconnect()}.bind(this));c.observe(a,{childList:!0,subtree:!0})}};c.prototype=d,d.constructor=c,a.Base=c,a.isBase=b,a.api.instance.base=d}(Polymer),function(a){function b(a){return a.__proto__}function c(a,b){var c="",d=!1;b&&(c=b.localName,d=b.hasAttribute("is"));var e=Platform.ShadowCSS.makeScopeSelector(c,d);return Platform.ShadowCSS.shimCssText(a,e)}var d=(window.logFlags||{},window.ShadowDOMPolyfill),e="element",f="controller",g={STYLE_SCOPE_ATTRIBUTE:e,installControllerStyles:function(){var a=this.findStyleScope();if(a&&!this.scopeHasNamedStyle(a,this.localName)){for(var c=b(this),d="";c&&c.element;)d+=c.element.cssTextForScope(f),c=b(c);d&&this.installScopeCssText(d,a)}},installScopeStyle:function(a,b,c){var c=c||this.findStyleScope(),b=b||"";if(c&&!this.scopeHasNamedStyle(c,this.localName+b)){var d="";if(a instanceof Array)for(var e,f=0,g=a.length;g>f&&(e=a[f]);f++)d+=e.textContent+"\n\n";else d=a.textContent;this.installScopeCssText(d,c,b)}},installScopeCssText:function(a,b,e){if(b=b||this.findStyleScope(),e=e||"",b){d&&(a=c(a,b.host));var g=this.element.cssTextToScopeStyle(a,f);Polymer.applyStyleToScope(g,b),this.styleCacheForScope(b)[this.localName+e]=!0}},findStyleScope:function(a){for(var b=a||this;b.parentNode;)b=b.parentNode;return b},scopeHasNamedStyle:function(a,b){var c=this.styleCacheForScope(a);
|
| -return c[b]},styleCacheForScope:function(a){if(d){var b=a.host?a.host.localName:a.localName;return h[b]||(h[b]={})}return a._scopeStyles=a._scopeStyles||{}}},h={};a.api.instance.styles=g}(Polymer),function(a){function b(a,b){if("string"!=typeof a){var c=b||document._currentScript;if(b=a,a=c&&c.parentNode&&c.parentNode.getAttribute?c.parentNode.getAttribute("name"):"",!a)throw"Element name could not be inferred."}if(f(a))throw"Already registered (Polymer) prototype for element "+a;e(a,b),d(a)}function c(a,b){i[a]=b}function d(a){i[a]&&(i[a].registerWhenReady(),delete i[a])}function e(a,b){return j[a]=b||{}}function f(a){return j[a]}function g(a,b){if("string"!=typeof b)return!1;var c=HTMLElement.getPrototypeForTag(b),d=c&&c.constructor;return d?CustomElements.instanceof?CustomElements.instanceof(a,d):a instanceof d:!1}var h=a.extend,i=(a.api,{}),j={};a.getRegisteredPrototype=f,a.waitingForPrototype=c,a.instanceOfType=g,window.Polymer=b,h(Polymer,a),Platform.consumeDeclarations&&Platform.consumeDeclarations(function(a){if(a)for(var c,d=0,e=a.length;e>d&&(c=a[d]);d++)b.apply(null,c)})}(Polymer),function(a){var b={resolveElementPaths:function(a){Polymer.urlResolver.resolveDom(a)},addResolvePathApi:function(){var a=this.getAttribute("assetpath")||"",b=new URL(a,this.ownerDocument.baseURI);this.prototype.resolvePath=function(a,c){var d=new URL(a,c||b);return d.href}}};a.api.declaration.path=b}(Polymer),function(a){function b(a,b){var c=new URL(a.getAttribute("href"),b).href;return"@import '"+c+"';"}function c(a,b){if(a){b===document&&(b=document.head),i&&(b=document.head);var c=d(a.textContent),e=a.getAttribute(h);e&&c.setAttribute(h,e);var f=b.firstElementChild;if(b===document.head){var g="style["+h+"]",j=document.head.querySelectorAll(g);j.length&&(f=j[j.length-1].nextElementSibling)}b.insertBefore(c,f)}}function d(a,b){b=b||document,b=b.createElement?b:b.ownerDocument;var c=b.createElement("style");return c.textContent=a,c}function e(a){return a&&a.__resource||""}function f(a,b){return q?q.call(a,b):void 0}var g=(window.logFlags||{},a.api.instance.styles),h=g.STYLE_SCOPE_ATTRIBUTE,i=window.ShadowDOMPolyfill,j="style",k="@import",l="link[rel=stylesheet]",m="global",n="polymer-scope",o={loadStyles:function(a){var b=this.fetchTemplate(),c=b&&this.templateContent();if(c){this.convertSheetsToStyles(c);var d=this.findLoadableStyles(c);if(d.length){var e=b.ownerDocument.baseURI;return Polymer.styleResolver.loadStyles(d,e,a)}}a&&a()},convertSheetsToStyles:function(a){for(var c,e,f=a.querySelectorAll(l),g=0,h=f.length;h>g&&(c=f[g]);g++)e=d(b(c,this.ownerDocument.baseURI),this.ownerDocument),this.copySheetAttributes(e,c),c.parentNode.replaceChild(e,c)},copySheetAttributes:function(a,b){for(var c,d=0,e=b.attributes,f=e.length;(c=e[d])&&f>d;d++)"rel"!==c.name&&"href"!==c.name&&a.setAttribute(c.name,c.value)},findLoadableStyles:function(a){var b=[];if(a)for(var c,d=a.querySelectorAll(j),e=0,f=d.length;f>e&&(c=d[e]);e++)c.textContent.match(k)&&b.push(c);return b},installSheets:function(){this.cacheSheets(),this.cacheStyles(),this.installLocalSheets(),this.installGlobalStyles()},cacheSheets:function(){this.sheets=this.findNodes(l),this.sheets.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},cacheStyles:function(){this.styles=this.findNodes(j+"["+n+"]"),this.styles.forEach(function(a){a.parentNode&&a.parentNode.removeChild(a)})},installLocalSheets:function(){var a=this.sheets.filter(function(a){return!a.hasAttribute(n)}),b=this.templateContent();if(b){var c="";if(a.forEach(function(a){c+=e(a)+"\n"}),c){var f=d(c,this.ownerDocument);b.insertBefore(f,b.firstChild)}}},findNodes:function(a,b){var c=this.querySelectorAll(a).array(),d=this.templateContent();if(d){var e=d.querySelectorAll(a).array();c=c.concat(e)}return b?c.filter(b):c},installGlobalStyles:function(){var a=this.styleForScope(m);c(a,document.head)},cssTextForScope:function(a){var b="",c="["+n+"="+a+"]",d=function(a){return f(a,c)},g=this.sheets.filter(d);g.forEach(function(a){b+=e(a)+"\n\n"});var h=this.styles.filter(d);return h.forEach(function(a){b+=a.textContent+"\n\n"}),b},styleForScope:function(a){var b=this.cssTextForScope(a);return this.cssTextToScopeStyle(b,a)},cssTextToScopeStyle:function(a,b){if(a){var c=d(a);return c.setAttribute(h,this.getAttribute("name")+"-"+b),c}}},p=HTMLElement.prototype,q=p.matches||p.matchesSelector||p.webkitMatchesSelector||p.mozMatchesSelector;a.api.declaration.styles=o,a.applyStyleToScope=c}(Polymer),function(a){var b=(window.logFlags||{},a.api.instance.events),c=b.EVENT_PREFIX,d={};["webkitAnimationStart","webkitAnimationEnd","webkitTransitionEnd","DOMFocusOut","DOMFocusIn","DOMMouseScroll"].forEach(function(a){d[a.toLowerCase()]=a});var e={parseHostEvents:function(){var a=this.prototype.eventDelegates;this.addAttributeDelegates(a)},addAttributeDelegates:function(a){for(var b,c=0;b=this.attributes[c];c++)this.hasEventPrefix(b.name)&&(a[this.removeEventPrefix(b.name)]=b.value.replace("{{","").replace("}}","").trim())},hasEventPrefix:function(a){return a&&"o"===a[0]&&"n"===a[1]&&"-"===a[2]},removeEventPrefix:function(a){return a.slice(f)},findController:function(a){for(;a.parentNode;){if(a.eventController)return a.eventController;a=a.parentNode}return a.host},getEventHandler:function(a,b,c){var d=this;return function(e){a&&a.PolymerBase||(a=d.findController(b));var f=[e,e.detail,e.currentTarget];a.dispatchMethod(a,c,f)}},prepareEventBinding:function(a,b){if(this.hasEventPrefix(b)){var c=this.removeEventPrefix(b);c=d[c]||c;var e=this;return function(b,d,f){function g(){return"{{ "+a+" }}"}var h=e.getEventHandler(void 0,d,a);return PolymerGestures.addEventListener(d,c,h),f?void 0:{open:g,discardChanges:g,close:function(){PolymerGestures.removeEventListener(d,c,h)}}}}}},f=c.length;a.api.declaration.events=e}(Polymer),function(a){var b={inferObservers:function(a){var b,c=a.observe;for(var d in a)"Changed"===d.slice(-7)&&(c||(c=a.observe={}),b=d.slice(0,-7),c[b]=c[b]||d)},explodeObservers:function(a){var b=a.observe;if(b){var c={};for(var d in b)for(var e,f=d.split(" "),g=0;e=f[g];g++)c[e]=b[d];a.observe=c}},optimizePropertyMaps:function(a){if(a.observe){var b=a._observeNames=[];for(var c in a.observe)for(var d,e=c.split(" "),f=0;d=e[f];f++)b.push(d)}if(a.publish){var b=a._publishNames=[];for(var c in a.publish)b.push(c)}if(a.computed){var b=a._computedNames=[];for(var c in a.computed)b.push(c)}},publishProperties:function(a,b){var c=a.publish;c&&(this.requireProperties(c,a,b),a._publishLC=this.lowerCaseMap(c))},requireProperties:function(a,b){b.reflect=b.reflect||{};for(var c in a){var d=a[c];d&&void 0!==d.reflect&&(b.reflect[c]=Boolean(d.reflect),d=d.value),void 0!==d&&(b[c]=d)}},lowerCaseMap:function(a){var b={};for(var c in a)b[c.toLowerCase()]=c;return b},createPropertyAccessor:function(a,b){var c=this.prototype,d=a+"_",e=a+"Observable_";c[d]=c[a],Object.defineProperty(c,a,{get:function(){var a=this[e];return a&&a.deliver(),this[d]},set:function(c){if(b)return this[d];var f=this[e];if(f)return void f.setValue(c);var g=this[d];return this[d]=c,this.emitPropertyChangeRecord(a,c,g),c},configurable:!0})},createPropertyAccessors:function(a){var b=a._computedNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)this.createPropertyAccessor(c,!0);var b=a._publishNames;if(b&&b.length)for(var c,d=0,e=b.length;e>d&&(c=b[d]);d++)a.computed&&a.computed[c]||this.createPropertyAccessor(c)}};a.api.declaration.properties=b}(Polymer),function(a){var b="attributes",c=/\s|,/,d={inheritAttributesObjects:function(a){this.inheritObject(a,"publishLC"),this.inheritObject(a,"_instanceAttributes")},publishAttributes:function(a){var d=this.getAttribute(b);if(d)for(var e,f=a.publish||(a.publish={}),g=d.split(c),h=0,i=g.length;i>h;h++)e=g[h].trim(),e&&void 0===f[e]&&(f[e]=void 0)},accumulateInstanceAttributes:function(){for(var a,b=this.prototype._instanceAttributes,c=this.attributes,d=0,e=c.length;e>d&&(a=c[d]);d++)this.isInstanceAttribute(a.name)&&(b[a.name]=a.value)},isInstanceAttribute:function(a){return!this.blackList[a]&&"on-"!==a.slice(0,3)},blackList:{name:1,"extends":1,constructor:1,noscript:1,assetpath:1,"cache-csstext":1}};d.blackList[b]=1,a.api.declaration.attributes=d}(Polymer),function(a){var b=a.api.declaration.events,c=new PolymerExpressions,d=c.prepareBinding;c.prepareBinding=function(a,e,f){return b.prepareEventBinding(a,e,f)||d.call(c,a,e,f)};var e={syntax:c,fetchTemplate:function(){return this.querySelector("template")},templateContent:function(){var a=this.fetchTemplate();return a&&a.content},installBindingDelegate:function(a){a&&(a.bindingDelegate=this.syntax)}};a.api.declaration.mdv=e}(Polymer),function(a){function b(a){if(!Object.__proto__){var b=Object.getPrototypeOf(a);a.__proto__=b,d(b)&&(b.__proto__=Object.getPrototypeOf(b))}}var c=a.api,d=a.isBase,e=a.extend,f=window.ShadowDOMPolyfill,g={register:function(a,b){this.buildPrototype(a,b),this.registerPrototype(a,b),this.publishConstructor()},buildPrototype:function(b,c){var d=a.getRegisteredPrototype(b),e=this.generateBasePrototype(c);this.desugarBeforeChaining(d,e),this.prototype=this.chainPrototypes(d,e),this.desugarAfterChaining(b,c)},desugarBeforeChaining:function(a,b){a.element=this,this.publishAttributes(a,b),this.publishProperties(a,b),this.inferObservers(a),this.explodeObservers(a)},chainPrototypes:function(a,c){this.inheritMetaData(a,c);var d=this.chainObject(a,c);return b(d),d},inheritMetaData:function(a,b){this.inheritObject("observe",a,b),this.inheritObject("publish",a,b),this.inheritObject("reflect",a,b),this.inheritObject("_publishLC",a,b),this.inheritObject("_instanceAttributes",a,b),this.inheritObject("eventDelegates",a,b)},desugarAfterChaining:function(a,b){this.optimizePropertyMaps(this.prototype),this.createPropertyAccessors(this.prototype),this.installBindingDelegate(this.fetchTemplate()),this.installSheets(),this.resolveElementPaths(this),this.accumulateInstanceAttributes(),this.parseHostEvents(),this.addResolvePathApi(),f&&Platform.ShadowCSS.shimStyling(this.templateContent(),a,b),this.prototype.registerCallback&&this.prototype.registerCallback(this)},publishConstructor:function(){var a=this.getAttribute("constructor");a&&(window[a]=this.ctor)},generateBasePrototype:function(a){var b=this.findBasePrototype(a);if(!b){var b=HTMLElement.getPrototypeForTag(a);b=this.ensureBaseApi(b),h[a]=b}return b},findBasePrototype:function(a){return h[a]},ensureBaseApi:function(a){if(a.PolymerBase)return a;var b=Object.create(a);return c.publish(c.instance,b),this.mixinMethod(b,a,c.instance.mdv,"bind"),b},mixinMethod:function(a,b,c,d){var e=function(a){return b[d].apply(this,a)};a[d]=function(){return this.mixinSuper=e,c[d].apply(this,arguments)}},inheritObject:function(a,b,c){var d=b[a]||{};b[a]=this.chainObject(d,c[a])},registerPrototype:function(a,b){var c={prototype:this.prototype},d=this.findTypeExtension(b);d&&(c.extends=d),HTMLElement.register(a,this.prototype),this.ctor=document.registerElement(a,c)},findTypeExtension:function(a){if(a&&a.indexOf("-")<0)return a;var b=this.findBasePrototype(a);return b.element?this.findTypeExtension(b.element.extends):void 0}},h={};g.chainObject=Object.__proto__?function(a,b){return a&&b&&a!==b&&(a.__proto__=b),a}:function(a,b){if(a&&b&&a!==b){var c=Object.create(b);a=e(c,a)}return a},c.declaration.prototype=g}(Polymer),function(a){function b(a){return document.contains(a)?j:i}function c(){return i.length?i[0]:j[0]}function d(a){f.waitToReady=!0,Platform.endOfMicrotask(function(){HTMLImports.whenReady(function(){f.addReadyCallback(a),f.waitToReady=!1,f.check()})})}function e(a){if(void 0===a)return void f.ready();var b=setTimeout(function(){f.ready()},a);Polymer.whenReady(function(){clearTimeout(b)})}var f={wait:function(a){a.__queue||(a.__queue={},g.push(a))},enqueue:function(a,c,d){var e=a.__queue&&!a.__queue.check;return e&&(b(a).push(a),a.__queue.check=c,a.__queue.go=d),0!==this.indexOf(a)},indexOf:function(a){var c=b(a).indexOf(a);return c>=0&&document.contains(a)&&(c+=HTMLImports.useNative||HTMLImports.ready?i.length:1e9),c},go:function(a){var b=this.remove(a);b&&(a.__queue.flushable=!0,this.addToFlushQueue(b),this.check())},remove:function(a){var c=this.indexOf(a);if(0===c)return b(a).shift()},check:function(){var a=this.nextElement();return a&&a.__queue.check.call(a),this.canReady()?(this.ready(),!0):void 0},nextElement:function(){return c()},canReady:function(){return!this.waitToReady&&this.isEmpty()},isEmpty:function(){for(var a,b=0,c=g.length;c>b&&(a=g[b]);b++)if(a.__queue&&!a.__queue.flushable)return;return!0},addToFlushQueue:function(a){h.push(a)},flush:function(){if(!this.flushing){this.flushing=!0;for(var a;h.length;)a=h.shift(),a.__queue.go.call(a),a.__queue=null;this.flushing=!1}},ready:function(){var a=CustomElements.ready;CustomElements.ready=!1,this.flush(),CustomElements.useNative||CustomElements.upgradeDocumentTree(document),CustomElements.ready=a,Platform.flush(),requestAnimationFrame(this.flushReadyCallbacks)},addReadyCallback:function(a){a&&k.push(a)},flushReadyCallbacks:function(){if(k)for(var a;k.length;)(a=k.shift())()},waitingFor:function(){for(var a,b=[],c=0,d=g.length;d>c&&(a=g[c]);c++)a.__queue&&!a.__queue.flushable&&b.push(a);return b},waitToReady:!0},g=[],h=[],i=[],j=[],k=[];a.elements=g,a.waitingFor=f.waitingFor.bind(f),a.forceReady=e,a.queue=f,a.whenReady=a.whenPolymerReady=d}(Polymer),function(a){function b(a){return Boolean(HTMLElement.getPrototypeForTag(a))}function c(a){return a&&a.indexOf("-")>=0}var d=a.extend,e=a.api,f=a.queue,g=a.whenReady,h=a.getRegisteredPrototype,i=a.waitingForPrototype,j=d(Object.create(HTMLElement.prototype),{createdCallback:function(){this.getAttribute("name")&&this.init()},init:function(){this.name=this.getAttribute("name"),this.extends=this.getAttribute("extends"),f.wait(this),this.loadResources(),this.registerWhenReady()},registerWhenReady:function(){this.registered||this.waitingForPrototype(this.name)||this.waitingForQueue()||this.waitingForResources()||f.go(this)},_register:function(){c(this.extends)&&!b(this.extends)&&console.warn("%s is attempting to extend %s, an unregistered element or one that was not registered with Polymer.",this.name,this.extends),this.register(this.name,this.extends),this.registered=!0},waitingForPrototype:function(a){return h(a)?void 0:(i(a,this),this.handleNoScript(a),!0)},handleNoScript:function(a){this.hasAttribute("noscript")&&!this.noscript&&(this.noscript=!0,Polymer(a))},waitingForResources:function(){return this._needsResources},waitingForQueue:function(){return f.enqueue(this,this.registerWhenReady,this._register)},loadResources:function(){this._needsResources=!0,this.loadStyles(function(){this._needsResources=!1,this.registerWhenReady()}.bind(this))}});e.publish(e.declaration,j),g(function(){document.body.removeAttribute("unresolved"),document.dispatchEvent(new CustomEvent("polymer-ready",{bubbles:!0}))}),document.registerElement("polymer-element",{prototype:j})}(Polymer),function(a){function b(a,b){a?(document.head.appendChild(a),d(b)):b&&b()}function c(a,c){if(a&&a.length){for(var d,e,f=document.createDocumentFragment(),g=0,h=a.length;h>g&&(d=a[g]);g++)e=document.createElement("link"),e.rel="import",e.href=d,f.appendChild(e);b(f,c)}else c&&c()}var d=a.whenPolymerReady;a.import=c,a.importElements=b}(Polymer),function(){var a=document.createElement("polymer-element");a.setAttribute("name","auto-binding"),a.setAttribute("extends","template"),a.init(),Polymer("auto-binding",{createdCallback:function(){this.syntax=this.bindingDelegate=this.makeSyntax(),Polymer.whenPolymerReady(function(){this.model=this,this.setAttribute("bind",""),this.async(function(){this.marshalNodeReferences(this.parentNode),this.fire("template-bound")})}.bind(this))},makeSyntax:function(){var a=Object.create(Polymer.api.declaration.events),b=this;a.findController=function(){return b.model};var c=new PolymerExpressions,d=c.prepareBinding;return c.prepareBinding=function(b,e,f){return a.prepareEventBinding(b,e,f)||d.call(c,b,e,f)},c}})}();
|
| -//# sourceMappingURL=polymer.js.map
|
| +// @version 0.5.1
|
| +window.PolymerGestures = {};
|
| +
|
| +(function(scope) {
|
| + var HAS_FULL_PATH = false;
|
| +
|
| + // test for full event path support
|
| + var pathTest = document.createElement('meta');
|
| + if (pathTest.createShadowRoot) {
|
| + var sr = pathTest.createShadowRoot();
|
| + var s = document.createElement('span');
|
| + sr.appendChild(s);
|
| + pathTest.addEventListener('testpath', function(ev) {
|
| + if (ev.path) {
|
| + // if the span is in the event path, then path[0] is the real source for all events
|
| + HAS_FULL_PATH = ev.path[0] === s;
|
| + }
|
| + ev.stopPropagation();
|
| + });
|
| + var ev = new CustomEvent('testpath', {bubbles: true});
|
| + // must add node to DOM to trigger event listener
|
| + document.head.appendChild(pathTest);
|
| + s.dispatchEvent(ev);
|
| + pathTest.parentNode.removeChild(pathTest);
|
| + sr = s = null;
|
| + }
|
| + pathTest = null;
|
| +
|
| + var target = {
|
| + shadow: function(inEl) {
|
| + if (inEl) {
|
| + return inEl.shadowRoot || inEl.webkitShadowRoot;
|
| + }
|
| + },
|
| + canTarget: function(shadow) {
|
| + return shadow && Boolean(shadow.elementFromPoint);
|
| + },
|
| + targetingShadow: function(inEl) {
|
| + var s = this.shadow(inEl);
|
| + if (this.canTarget(s)) {
|
| + return s;
|
| + }
|
| + },
|
| + olderShadow: function(shadow) {
|
| + var os = shadow.olderShadowRoot;
|
| + if (!os) {
|
| + var se = shadow.querySelector('shadow');
|
| + if (se) {
|
| + os = se.olderShadowRoot;
|
| + }
|
| + }
|
| + return os;
|
| + },
|
| + allShadows: function(element) {
|
| + var shadows = [], s = this.shadow(element);
|
| + while(s) {
|
| + shadows.push(s);
|
| + s = this.olderShadow(s);
|
| + }
|
| + return shadows;
|
| + },
|
| + searchRoot: function(inRoot, x, y) {
|
| + var t, st, sr, os;
|
| + if (inRoot) {
|
| + t = inRoot.elementFromPoint(x, y);
|
| + if (t) {
|
| + // found element, check if it has a ShadowRoot
|
| + sr = this.targetingShadow(t);
|
| + } else if (inRoot !== document) {
|
| + // check for sibling roots
|
| + sr = this.olderShadow(inRoot);
|
| + }
|
| + // search other roots, fall back to light dom element
|
| + return this.searchRoot(sr, x, y) || t;
|
| + }
|
| + },
|
| + owner: function(element) {
|
| + if (!element) {
|
| + return document;
|
| + }
|
| + var s = element;
|
| + // walk up until you hit the shadow root or document
|
| + while (s.parentNode) {
|
| + s = s.parentNode;
|
| + }
|
| + // the owner element is expected to be a Document or ShadowRoot
|
| + if (s.nodeType != Node.DOCUMENT_NODE && s.nodeType != Node.DOCUMENT_FRAGMENT_NODE) {
|
| + s = document;
|
| + }
|
| + return s;
|
| + },
|
| + findTarget: function(inEvent) {
|
| + if (HAS_FULL_PATH && inEvent.path && inEvent.path.length) {
|
| + return inEvent.path[0];
|
| + }
|
| + var x = inEvent.clientX, y = inEvent.clientY;
|
| + // if the listener is in the shadow root, it is much faster to start there
|
| + var s = this.owner(inEvent.target);
|
| + // if x, y is not in this root, fall back to document search
|
| + if (!s.elementFromPoint(x, y)) {
|
| + s = document;
|
| + }
|
| + return this.searchRoot(s, x, y);
|
| + },
|
| + findTouchAction: function(inEvent) {
|
| + var n;
|
| + if (HAS_FULL_PATH && inEvent.path && inEvent.path.length) {
|
| + var path = inEvent.path;
|
| + for (var i = 0; i < path.length; i++) {
|
| + n = path[i];
|
| + if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
|
| + return n.getAttribute('touch-action');
|
| + }
|
| + }
|
| + } else {
|
| + n = inEvent.target;
|
| + while(n) {
|
| + if (n.nodeType === Node.ELEMENT_NODE && n.hasAttribute('touch-action')) {
|
| + return n.getAttribute('touch-action');
|
| + }
|
| + n = n.parentNode || n.host;
|
| + }
|
| + }
|
| + // auto is default
|
| + return "auto";
|
| + },
|
| + LCA: function(a, b) {
|
| + if (a === b) {
|
| + return a;
|
| + }
|
| + if (a && !b) {
|
| + return a;
|
| + }
|
| + if (b && !a) {
|
| + return b;
|
| + }
|
| + if (!b && !a) {
|
| + return document;
|
| + }
|
| + // fast case, a is a direct descendant of b or vice versa
|
| + if (a.contains && a.contains(b)) {
|
| + return a;
|
| + }
|
| + if (b.contains && b.contains(a)) {
|
| + return b;
|
| + }
|
| + var adepth = this.depth(a);
|
| + var bdepth = this.depth(b);
|
| + var d = adepth - bdepth;
|
| + if (d >= 0) {
|
| + a = this.walk(a, d);
|
| + } else {
|
| + b = this.walk(b, -d);
|
| + }
|
| + while (a && b && a !== b) {
|
| + a = a.parentNode || a.host;
|
| + b = b.parentNode || b.host;
|
| + }
|
| + return a;
|
| + },
|
| + walk: function(n, u) {
|
| + for (var i = 0; n && (i < u); i++) {
|
| + n = n.parentNode || n.host;
|
| + }
|
| + return n;
|
| + },
|
| + depth: function(n) {
|
| + var d = 0;
|
| + while(n) {
|
| + d++;
|
| + n = n.parentNode || n.host;
|
| + }
|
| + return d;
|
| + },
|
| + deepContains: function(a, b) {
|
| + var common = this.LCA(a, b);
|
| + // if a is the common ancestor, it must "deeply" contain b
|
| + return common === a;
|
| + },
|
| + insideNode: function(node, x, y) {
|
| + var rect = node.getBoundingClientRect();
|
| + return (rect.left <= x) && (x <= rect.right) && (rect.top <= y) && (y <= rect.bottom);
|
| + },
|
| + path: function(event) {
|
| + var p;
|
| + if (HAS_FULL_PATH && event.path && event.path.length) {
|
| + p = event.path;
|
| + } else {
|
| + p = [];
|
| + var n = this.findTarget(event);
|
| + while (n) {
|
| + p.push(n);
|
| + n = n.parentNode || n.host;
|
| + }
|
| + }
|
| + return p;
|
| + }
|
| + };
|
| + scope.targetFinding = target;
|
| + /**
|
| + * Given an event, finds the "deepest" node that could have been the original target before ShadowDOM retargetting
|
| + *
|
| + * @param {Event} Event An event object with clientX and clientY properties
|
| + * @return {Element} The probable event origninator
|
| + */
|
| + scope.findTarget = target.findTarget.bind(target);
|
| + /**
|
| + * Determines if the "container" node deeply contains the "containee" node, including situations where the "containee" is contained by one or more ShadowDOM
|
| + * roots.
|
| + *
|
| + * @param {Node} container
|
| + * @param {Node} containee
|
| + * @return {Boolean}
|
| + */
|
| + scope.deepContains = target.deepContains.bind(target);
|
| +
|
| + /**
|
| + * Determines if the x/y position is inside the given node.
|
| + *
|
| + * Example:
|
| + *
|
| + * function upHandler(event) {
|
| + * var innode = PolymerGestures.insideNode(event.target, event.clientX, event.clientY);
|
| + * if (innode) {
|
| + * // wait for tap?
|
| + * } else {
|
| + * // tap will never happen
|
| + * }
|
| + * }
|
| + *
|
| + * @param {Node} node
|
| + * @param {Number} x Screen X position
|
| + * @param {Number} y screen Y position
|
| + * @return {Boolean}
|
| + */
|
| + scope.insideNode = target.insideNode;
|
| +
|
| +})(window.PolymerGestures);
|
| +
|
| +(function() {
|
| + function shadowSelector(v) {
|
| + return 'html /deep/ ' + selector(v);
|
| + }
|
| + function selector(v) {
|
| + return '[touch-action="' + v + '"]';
|
| + }
|
| + function rule(v) {
|
| + return '{ -ms-touch-action: ' + v + '; touch-action: ' + v + ';}';
|
| + }
|
| + var attrib2css = [
|
| + 'none',
|
| + 'auto',
|
| + 'pan-x',
|
| + 'pan-y',
|
| + {
|
| + rule: 'pan-x pan-y',
|
| + selectors: [
|
| + 'pan-x pan-y',
|
| + 'pan-y pan-x'
|
| + ]
|
| + },
|
| + 'manipulation'
|
| + ];
|
| + var styles = '';
|
| + // only install stylesheet if the browser has touch action support
|
| + var hasTouchAction = typeof document.head.style.touchAction === 'string';
|
| + // only add shadow selectors if shadowdom is supported
|
| + var hasShadowRoot = !window.ShadowDOMPolyfill && document.head.createShadowRoot;
|
| +
|
| + if (hasTouchAction) {
|
| + attrib2css.forEach(function(r) {
|
| + if (String(r) === r) {
|
| + styles += selector(r) + rule(r) + '\n';
|
| + if (hasShadowRoot) {
|
| + styles += shadowSelector(r) + rule(r) + '\n';
|
| + }
|
| + } else {
|
| + styles += r.selectors.map(selector) + rule(r.rule) + '\n';
|
| + if (hasShadowRoot) {
|
| + styles += r.selectors.map(shadowSelector) + rule(r.rule) + '\n';
|
| + }
|
| + }
|
| + });
|
| +
|
| + var el = document.createElement('style');
|
| + el.textContent = styles;
|
| + document.head.appendChild(el);
|
| + }
|
| +})();
|
| +
|
| +/**
|
| + * This is the constructor for new PointerEvents.
|
| + *
|
| + * New Pointer Events must be given a type, and an optional dictionary of
|
| + * initialization properties.
|
| + *
|
| + * Due to certain platform requirements, events returned from the constructor
|
| + * identify as MouseEvents.
|
| + *
|
| + * @constructor
|
| + * @param {String} inType The type of the event to create.
|
| + * @param {Object} [inDict] An optional dictionary of initial event properties.
|
| + * @return {Event} A new PointerEvent of type `inType` and initialized with properties from `inDict`.
|
| + */
|
| +(function(scope) {
|
| +
|
| + var MOUSE_PROPS = [
|
| + 'bubbles',
|
| + 'cancelable',
|
| + 'view',
|
| + 'detail',
|
| + 'screenX',
|
| + 'screenY',
|
| + 'clientX',
|
| + 'clientY',
|
| + 'ctrlKey',
|
| + 'altKey',
|
| + 'shiftKey',
|
| + 'metaKey',
|
| + 'button',
|
| + 'relatedTarget',
|
| + 'pageX',
|
| + 'pageY'
|
| + ];
|
| +
|
| + var MOUSE_DEFAULTS = [
|
| + false,
|
| + false,
|
| + null,
|
| + null,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + false,
|
| + false,
|
| + false,
|
| + false,
|
| + 0,
|
| + null,
|
| + 0,
|
| + 0
|
| + ];
|
| +
|
| + var NOP_FACTORY = function(){ return function(){}; };
|
| +
|
| + var eventFactory = {
|
| + // TODO(dfreedm): this is overridden by tap recognizer, needs review
|
| + preventTap: NOP_FACTORY,
|
| + makeBaseEvent: function(inType, inDict) {
|
| + var e = document.createEvent('Event');
|
| + e.initEvent(inType, inDict.bubbles || false, inDict.cancelable || false);
|
| + e.preventTap = eventFactory.preventTap(e);
|
| + return e;
|
| + },
|
| + makeGestureEvent: function(inType, inDict) {
|
| + inDict = inDict || Object.create(null);
|
| +
|
| + var e = this.makeBaseEvent(inType, inDict);
|
| + for (var i = 0, keys = Object.keys(inDict), k; i < keys.length; i++) {
|
| + k = keys[i];
|
| + e[k] = inDict[k];
|
| + }
|
| + return e;
|
| + },
|
| + makePointerEvent: function(inType, inDict) {
|
| + inDict = inDict || Object.create(null);
|
| +
|
| + var e = this.makeBaseEvent(inType, inDict);
|
| + // define inherited MouseEvent properties
|
| + for(var i = 0, p; i < MOUSE_PROPS.length; i++) {
|
| + p = MOUSE_PROPS[i];
|
| + e[p] = inDict[p] || MOUSE_DEFAULTS[i];
|
| + }
|
| + e.buttons = inDict.buttons || 0;
|
| +
|
| + // Spec requires that pointers without pressure specified use 0.5 for down
|
| + // state and 0 for up state.
|
| + var pressure = 0;
|
| + if (inDict.pressure) {
|
| + pressure = inDict.pressure;
|
| + } else {
|
| + pressure = e.buttons ? 0.5 : 0;
|
| + }
|
| +
|
| + // add x/y properties aliased to clientX/Y
|
| + e.x = e.clientX;
|
| + e.y = e.clientY;
|
| +
|
| + // define the properties of the PointerEvent interface
|
| + e.pointerId = inDict.pointerId || 0;
|
| + e.width = inDict.width || 0;
|
| + e.height = inDict.height || 0;
|
| + e.pressure = pressure;
|
| + e.tiltX = inDict.tiltX || 0;
|
| + e.tiltY = inDict.tiltY || 0;
|
| + e.pointerType = inDict.pointerType || '';
|
| + e.hwTimestamp = inDict.hwTimestamp || 0;
|
| + e.isPrimary = inDict.isPrimary || false;
|
| + e._source = inDict._source || '';
|
| + return e;
|
| + }
|
| + };
|
| +
|
| + scope.eventFactory = eventFactory;
|
| +})(window.PolymerGestures);
|
| +
|
| +/**
|
| + * This module implements an map of pointer states
|
| + */
|
| +(function(scope) {
|
| + var USE_MAP = window.Map && window.Map.prototype.forEach;
|
| + var POINTERS_FN = function(){ return this.size; };
|
| + function PointerMap() {
|
| + if (USE_MAP) {
|
| + var m = new Map();
|
| + m.pointers = POINTERS_FN;
|
| + return m;
|
| + } else {
|
| + this.keys = [];
|
| + this.values = [];
|
| + }
|
| + }
|
| +
|
| + PointerMap.prototype = {
|
| + set: function(inId, inEvent) {
|
| + var i = this.keys.indexOf(inId);
|
| + if (i > -1) {
|
| + this.values[i] = inEvent;
|
| + } else {
|
| + this.keys.push(inId);
|
| + this.values.push(inEvent);
|
| + }
|
| + },
|
| + has: function(inId) {
|
| + return this.keys.indexOf(inId) > -1;
|
| + },
|
| + 'delete': function(inId) {
|
| + var i = this.keys.indexOf(inId);
|
| + if (i > -1) {
|
| + this.keys.splice(i, 1);
|
| + this.values.splice(i, 1);
|
| + }
|
| + },
|
| + get: function(inId) {
|
| + var i = this.keys.indexOf(inId);
|
| + return this.values[i];
|
| + },
|
| + clear: function() {
|
| + this.keys.length = 0;
|
| + this.values.length = 0;
|
| + },
|
| + // return value, key, map
|
| + forEach: function(callback, thisArg) {
|
| + this.values.forEach(function(v, i) {
|
| + callback.call(thisArg, v, this.keys[i], this);
|
| + }, this);
|
| + },
|
| + pointers: function() {
|
| + return this.keys.length;
|
| + }
|
| + };
|
| +
|
| + scope.PointerMap = PointerMap;
|
| +})(window.PolymerGestures);
|
| +
|
| +(function(scope) {
|
| + var CLONE_PROPS = [
|
| + // MouseEvent
|
| + 'bubbles',
|
| + 'cancelable',
|
| + 'view',
|
| + 'detail',
|
| + 'screenX',
|
| + 'screenY',
|
| + 'clientX',
|
| + 'clientY',
|
| + 'ctrlKey',
|
| + 'altKey',
|
| + 'shiftKey',
|
| + 'metaKey',
|
| + 'button',
|
| + 'relatedTarget',
|
| + // DOM Level 3
|
| + 'buttons',
|
| + // PointerEvent
|
| + 'pointerId',
|
| + 'width',
|
| + 'height',
|
| + 'pressure',
|
| + 'tiltX',
|
| + 'tiltY',
|
| + 'pointerType',
|
| + 'hwTimestamp',
|
| + 'isPrimary',
|
| + // event instance
|
| + 'type',
|
| + 'target',
|
| + 'currentTarget',
|
| + 'which',
|
| + 'pageX',
|
| + 'pageY',
|
| + 'timeStamp',
|
| + // gesture addons
|
| + 'preventTap',
|
| + 'tapPrevented',
|
| + '_source'
|
| + ];
|
| +
|
| + var CLONE_DEFAULTS = [
|
| + // MouseEvent
|
| + false,
|
| + false,
|
| + null,
|
| + null,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + false,
|
| + false,
|
| + false,
|
| + false,
|
| + 0,
|
| + null,
|
| + // DOM Level 3
|
| + 0,
|
| + // PointerEvent
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + '',
|
| + 0,
|
| + false,
|
| + // event instance
|
| + '',
|
| + null,
|
| + null,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + 0,
|
| + function(){},
|
| + false
|
| + ];
|
| +
|
| + var HAS_SVG_INSTANCE = (typeof SVGElementInstance !== 'undefined');
|
| +
|
| + var eventFactory = scope.eventFactory;
|
| +
|
| + // set of recognizers to run for the currently handled event
|
| + var currentGestures;
|
| +
|
| + /**
|
| + * This module is for normalizing events. Mouse and Touch events will be
|
| + * collected here, and fire PointerEvents that have the same semantics, no
|
| + * matter the source.
|
| + * Events fired:
|
| + * - pointerdown: a pointing is added
|
| + * - pointerup: a pointer is removed
|
| + * - pointermove: a pointer is moved
|
| + * - pointerover: a pointer crosses into an element
|
| + * - pointerout: a pointer leaves an element
|
| + * - pointercancel: a pointer will no longer generate events
|
| + */
|
| + var dispatcher = {
|
| + IS_IOS: false,
|
| + pointermap: new scope.PointerMap(),
|
| + requiredGestures: new scope.PointerMap(),
|
| + eventMap: Object.create(null),
|
| + // Scope objects for native events.
|
| + // This exists for ease of testing.
|
| + eventSources: Object.create(null),
|
| + eventSourceList: [],
|
| + gestures: [],
|
| + // map gesture event -> {listeners: int, index: gestures[int]}
|
| + dependencyMap: {
|
| + // make sure down and up are in the map to trigger "register"
|
| + down: {listeners: 0, index: -1},
|
| + up: {listeners: 0, index: -1}
|
| + },
|
| + gestureQueue: [],
|
| + /**
|
| + * Add a new event source that will generate pointer events.
|
| + *
|
| + * `inSource` must contain an array of event names named `events`, and
|
| + * functions with the names specified in the `events` array.
|
| + * @param {string} name A name for the event source
|
| + * @param {Object} source A new source of platform events.
|
| + */
|
| + registerSource: function(name, source) {
|
| + var s = source;
|
| + var newEvents = s.events;
|
| + if (newEvents) {
|
| + newEvents.forEach(function(e) {
|
| + if (s[e]) {
|
| + this.eventMap[e] = s[e].bind(s);
|
| + }
|
| + }, this);
|
| + this.eventSources[name] = s;
|
| + this.eventSourceList.push(s);
|
| + }
|
| + },
|
| + registerGesture: function(name, source) {
|
| + var obj = Object.create(null);
|
| + obj.listeners = 0;
|
| + obj.index = this.gestures.length;
|
| + for (var i = 0, g; i < source.exposes.length; i++) {
|
| + g = source.exposes[i].toLowerCase();
|
| + this.dependencyMap[g] = obj;
|
| + }
|
| + this.gestures.push(source);
|
| + },
|
| + register: function(element, initial) {
|
| + var l = this.eventSourceList.length;
|
| + for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
|
| + // call eventsource register
|
| + es.register.call(es, element, initial);
|
| + }
|
| + },
|
| + unregister: function(element) {
|
| + var l = this.eventSourceList.length;
|
| + for (var i = 0, es; (i < l) && (es = this.eventSourceList[i]); i++) {
|
| + // call eventsource register
|
| + es.unregister.call(es, element);
|
| + }
|
| + },
|
| + // EVENTS
|
| + down: function(inEvent) {
|
| + this.requiredGestures.set(inEvent.pointerId, currentGestures);
|
| + this.fireEvent('down', inEvent);
|
| + },
|
| + move: function(inEvent) {
|
| + // pipe move events into gesture queue directly
|
| + inEvent.type = 'move';
|
| + this.fillGestureQueue(inEvent);
|
| + },
|
| + up: function(inEvent) {
|
| + this.fireEvent('up', inEvent);
|
| + this.requiredGestures.delete(inEvent.pointerId);
|
| + },
|
| + cancel: function(inEvent) {
|
| + inEvent.tapPrevented = true;
|
| + this.fireEvent('up', inEvent);
|
| + this.requiredGestures.delete(inEvent.pointerId);
|
| + },
|
| + addGestureDependency: function(node, currentGestures) {
|
| + var gesturesWanted = node._pgEvents;
|
| + if (gesturesWanted && currentGestures) {
|
| + var gk = Object.keys(gesturesWanted);
|
| + for (var i = 0, r, ri, g; i < gk.length; i++) {
|
| + // gesture
|
| + g = gk[i];
|
| + if (gesturesWanted[g] > 0) {
|
| + // lookup gesture recognizer
|
| + r = this.dependencyMap[g];
|
| + // recognizer index
|
| + ri = r ? r.index : -1;
|
| + currentGestures[ri] = true;
|
| + }
|
| + }
|
| + }
|
| + },
|
| + // LISTENER LOGIC
|
| + eventHandler: function(inEvent) {
|
| + // This is used to prevent multiple dispatch of events from
|
| + // platform events. This can happen when two elements in different scopes
|
| + // are set up to create pointer events, which is relevant to Shadow DOM.
|
| +
|
| + var type = inEvent.type;
|
| +
|
| + // only generate the list of desired events on "down"
|
| + if (type === 'touchstart' || type === 'mousedown' || type === 'pointerdown' || type === 'MSPointerDown') {
|
| + if (!inEvent._handledByPG) {
|
| + currentGestures = {};
|
| + }
|
| +
|
| + // in IOS mode, there is only a listener on the document, so this is not re-entrant
|
| + if (this.IS_IOS) {
|
| + var ev = inEvent;
|
| + if (type === 'touchstart') {
|
| + var ct = inEvent.changedTouches[0];
|
| + // set up a fake event to give to the path builder
|
| + ev = {target: inEvent.target, clientX: ct.clientX, clientY: ct.clientY, path: inEvent.path};
|
| + }
|
| + // use event path if available, otherwise build a path from target finding
|
| + var nodes = inEvent.path || scope.targetFinding.path(ev);
|
| + for (var i = 0, n; i < nodes.length; i++) {
|
| + n = nodes[i];
|
| + this.addGestureDependency(n, currentGestures);
|
| + }
|
| + } else {
|
| + this.addGestureDependency(inEvent.currentTarget, currentGestures);
|
| + }
|
| + }
|
| +
|
| + if (inEvent._handledByPG) {
|
| + return;
|
| + }
|
| + var fn = this.eventMap && this.eventMap[type];
|
| + if (fn) {
|
| + fn(inEvent);
|
| + }
|
| + inEvent._handledByPG = true;
|
| + },
|
| + // set up event listeners
|
| + listen: function(target, events) {
|
| + for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
|
| + this.addEvent(target, e);
|
| + }
|
| + },
|
| + // remove event listeners
|
| + unlisten: function(target, events) {
|
| + for (var i = 0, l = events.length, e; (i < l) && (e = events[i]); i++) {
|
| + this.removeEvent(target, e);
|
| + }
|
| + },
|
| + addEvent: function(target, eventName) {
|
| + target.addEventListener(eventName, this.boundHandler);
|
| + },
|
| + removeEvent: function(target, eventName) {
|
| + target.removeEventListener(eventName, this.boundHandler);
|
| + },
|
| + // EVENT CREATION AND TRACKING
|
| + /**
|
| + * Creates a new Event of type `inType`, based on the information in
|
| + * `inEvent`.
|
| + *
|
| + * @param {string} inType A string representing the type of event to create
|
| + * @param {Event} inEvent A platform event with a target
|
| + * @return {Event} A PointerEvent of type `inType`
|
| + */
|
| + makeEvent: function(inType, inEvent) {
|
| + var e = eventFactory.makePointerEvent(inType, inEvent);
|
| + e.preventDefault = inEvent.preventDefault;
|
| + e.tapPrevented = inEvent.tapPrevented;
|
| + e._target = e._target || inEvent.target;
|
| + return e;
|
| + },
|
| + // make and dispatch an event in one call
|
| + fireEvent: function(inType, inEvent) {
|
| + var e = this.makeEvent(inType, inEvent);
|
| + return this.dispatchEvent(e);
|
| + },
|
| + /**
|
| + * Returns a snapshot of inEvent, with writable properties.
|
| + *
|
| + * @param {Event} inEvent An event that contains properties to copy.
|
| + * @return {Object} An object containing shallow copies of `inEvent`'s
|
| + * properties.
|
| + */
|
| + cloneEvent: function(inEvent) {
|
| + var eventCopy = Object.create(null), p;
|
| + for (var i = 0; i < CLONE_PROPS.length; i++) {
|
| + p = CLONE_PROPS[i];
|
| + eventCopy[p] = inEvent[p] || CLONE_DEFAULTS[i];
|
| + // Work around SVGInstanceElement shadow tree
|
| + // Return the <use> element that is represented by the instance for Safari, Chrome, IE.
|
| + // This is the behavior implemented by Firefox.
|
| + if (p === 'target' || p === 'relatedTarget') {
|
| + if (HAS_SVG_INSTANCE && eventCopy[p] instanceof SVGElementInstance) {
|
| + eventCopy[p] = eventCopy[p].correspondingUseElement;
|
| + }
|
| + }
|
| + }
|
| + // keep the semantics of preventDefault
|
| + eventCopy.preventDefault = function() {
|
| + inEvent.preventDefault();
|
| + };
|
| + return eventCopy;
|
| + },
|
| + /**
|
| + * Dispatches the event to its target.
|
| + *
|
| + * @param {Event} inEvent The event to be dispatched.
|
| + * @return {Boolean} True if an event handler returns true, false otherwise.
|
| + */
|
| + dispatchEvent: function(inEvent) {
|
| + var t = inEvent._target;
|
| + if (t) {
|
| + t.dispatchEvent(inEvent);
|
| + // clone the event for the gesture system to process
|
| + // clone after dispatch to pick up gesture prevention code
|
| + var clone = this.cloneEvent(inEvent);
|
| + clone.target = t;
|
| + this.fillGestureQueue(clone);
|
| + }
|
| + },
|
| + gestureTrigger: function() {
|
| + // process the gesture queue
|
| + for (var i = 0, e, rg; i < this.gestureQueue.length; i++) {
|
| + e = this.gestureQueue[i];
|
| + rg = e._requiredGestures;
|
| + if (rg) {
|
| + for (var j = 0, g, fn; j < this.gestures.length; j++) {
|
| + // only run recognizer if an element in the source event's path is listening for those gestures
|
| + if (rg[j]) {
|
| + g = this.gestures[j];
|
| + fn = g[e.type];
|
| + if (fn) {
|
| + fn.call(g, e);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + this.gestureQueue.length = 0;
|
| + },
|
| + fillGestureQueue: function(ev) {
|
| + // only trigger the gesture queue once
|
| + if (!this.gestureQueue.length) {
|
| + requestAnimationFrame(this.boundGestureTrigger);
|
| + }
|
| + ev._requiredGestures = this.requiredGestures.get(ev.pointerId);
|
| + this.gestureQueue.push(ev);
|
| + }
|
| + };
|
| + dispatcher.boundHandler = dispatcher.eventHandler.bind(dispatcher);
|
| + dispatcher.boundGestureTrigger = dispatcher.gestureTrigger.bind(dispatcher);
|
| + scope.dispatcher = dispatcher;
|
| +
|
| + /**
|
| + * Listen for `gesture` on `node` with the `handler` function
|
| + *
|
| + * If `handler` is the first listener for `gesture`, the underlying gesture recognizer is then enabled.
|
| + *
|
| + * @param {Element} node
|
| + * @param {string} gesture
|
| + * @return Boolean `gesture` is a valid gesture
|
| + */
|
| + scope.activateGesture = function(node, gesture) {
|
| + var g = gesture.toLowerCase();
|
| + var dep = dispatcher.dependencyMap[g];
|
| + if (dep) {
|
| + var recognizer = dispatcher.gestures[dep.index];
|
| + if (!node._pgListeners) {
|
| + dispatcher.register(node);
|
| + node._pgListeners = 0;
|
| + }
|
| + // TODO(dfreedm): re-evaluate bookkeeping to avoid using attributes
|
| + if (recognizer) {
|
| + var touchAction = recognizer.defaultActions && recognizer.defaultActions[g];
|
| + var actionNode;
|
| + switch(node.nodeType) {
|
| + case Node.ELEMENT_NODE:
|
| + actionNode = node;
|
| + break;
|
| + case Node.DOCUMENT_FRAGMENT_NODE:
|
| + actionNode = node.host;
|
| + break;
|
| + default:
|
| + actionNode = null;
|
| + break;
|
| + }
|
| + if (touchAction && actionNode && !actionNode.hasAttribute('touch-action')) {
|
| + actionNode.setAttribute('touch-action', touchAction);
|
| + }
|
| + }
|
| + if (!node._pgEvents) {
|
| + node._pgEvents = {};
|
| + }
|
| + node._pgEvents[g] = (node._pgEvents[g] || 0) + 1;
|
| + node._pgListeners++;
|
| + }
|
| + return Boolean(dep);
|
| + };
|
| +
|
| + /**
|
| + *
|
| + * Listen for `gesture` from `node` with `handler` function.
|
| + *
|
| + * @param {Element} node
|
| + * @param {string} gesture
|
| + * @param {Function} handler
|
| + * @param {Boolean} capture
|
| + */
|
| + scope.addEventListener = function(node, gesture, handler, capture) {
|
| + if (handler) {
|
| + scope.activateGesture(node, gesture);
|
| + node.addEventListener(gesture, handler, capture);
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * Tears down the gesture configuration for `node`
|
| + *
|
| + * If `handler` is the last listener for `gesture`, the underlying gesture recognizer is disabled.
|
| + *
|
| + * @param {Element} node
|
| + * @param {string} gesture
|
| + * @return Boolean `gesture` is a valid gesture
|
| + */
|
| + scope.deactivateGesture = function(node, gesture) {
|
| + var g = gesture.toLowerCase();
|
| + var dep = dispatcher.dependencyMap[g];
|
| + if (dep) {
|
| + if (node._pgListeners > 0) {
|
| + node._pgListeners--;
|
| + }
|
| + if (node._pgListeners === 0) {
|
| + dispatcher.unregister(node);
|
| + }
|
| + if (node._pgEvents) {
|
| + if (node._pgEvents[g] > 0) {
|
| + node._pgEvents[g]--;
|
| + } else {
|
| + node._pgEvents[g] = 0;
|
| + }
|
| + }
|
| + }
|
| + return Boolean(dep);
|
| + };
|
| +
|
| + /**
|
| + * Stop listening for `gesture` from `node` with `handler` function.
|
| + *
|
| + * @param {Element} node
|
| + * @param {string} gesture
|
| + * @param {Function} handler
|
| + * @param {Boolean} capture
|
| + */
|
| + scope.removeEventListener = function(node, gesture, handler, capture) {
|
| + if (handler) {
|
| + scope.deactivateGesture(node, gesture);
|
| + node.removeEventListener(gesture, handler, capture);
|
| + }
|
| + };
|
| +})(window.PolymerGestures);
|
| +
|
| +(function (scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var pointermap = dispatcher.pointermap;
|
| + // radius around touchend that swallows mouse events
|
| + var DEDUP_DIST = 25;
|
| +
|
| + var WHICH_TO_BUTTONS = [0, 1, 4, 2];
|
| +
|
| + var CURRENT_BUTTONS = 0;
|
| + var HAS_BUTTONS = false;
|
| + try {
|
| + HAS_BUTTONS = new MouseEvent('test', {buttons: 1}).buttons === 1;
|
| + } catch (e) {}
|
| +
|
| + // handler block for native mouse events
|
| + var mouseEvents = {
|
| + POINTER_ID: 1,
|
| + POINTER_TYPE: 'mouse',
|
| + events: [
|
| + 'mousedown',
|
| + 'mousemove',
|
| + 'mouseup'
|
| + ],
|
| + exposes: [
|
| + 'down',
|
| + 'up',
|
| + 'move'
|
| + ],
|
| + register: function(target) {
|
| + dispatcher.listen(target, this.events);
|
| + },
|
| + unregister: function(target) {
|
| + if (target === document) {
|
| + return;
|
| + }
|
| + dispatcher.unlisten(target, this.events);
|
| + },
|
| + lastTouches: [],
|
| + // collide with the global mouse listener
|
| + isEventSimulatedFromTouch: function(inEvent) {
|
| + var lts = this.lastTouches;
|
| + var x = inEvent.clientX, y = inEvent.clientY;
|
| + for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) {
|
| + // simulated mouse events will be swallowed near a primary touchend
|
| + var dx = Math.abs(x - t.x), dy = Math.abs(y - t.y);
|
| + if (dx <= DEDUP_DIST && dy <= DEDUP_DIST) {
|
| + return true;
|
| + }
|
| + }
|
| + },
|
| + prepareEvent: function(inEvent) {
|
| + var e = dispatcher.cloneEvent(inEvent);
|
| + e.pointerId = this.POINTER_ID;
|
| + e.isPrimary = true;
|
| + e.pointerType = this.POINTER_TYPE;
|
| + e._source = 'mouse';
|
| + if (!HAS_BUTTONS) {
|
| + var type = inEvent.type;
|
| + var bit = WHICH_TO_BUTTONS[inEvent.which] || 0;
|
| + if (type === 'mousedown') {
|
| + CURRENT_BUTTONS |= bit;
|
| + } else if (type === 'mouseup') {
|
| + CURRENT_BUTTONS &= ~bit;
|
| + }
|
| + e.buttons = CURRENT_BUTTONS;
|
| + }
|
| + return e;
|
| + },
|
| + mousedown: function(inEvent) {
|
| + if (!this.isEventSimulatedFromTouch(inEvent)) {
|
| + var p = pointermap.has(this.POINTER_ID);
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = scope.findTarget(inEvent);
|
| + pointermap.set(this.POINTER_ID, e.target);
|
| + dispatcher.down(e);
|
| + }
|
| + },
|
| + mousemove: function(inEvent) {
|
| + if (!this.isEventSimulatedFromTouch(inEvent)) {
|
| + var target = pointermap.get(this.POINTER_ID);
|
| + if (target) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = target;
|
| + // handle case where we missed a mouseup
|
| + if ((HAS_BUTTONS ? e.buttons : e.which) === 0) {
|
| + if (!HAS_BUTTONS) {
|
| + CURRENT_BUTTONS = e.buttons = 0;
|
| + }
|
| + dispatcher.cancel(e);
|
| + this.cleanupMouse(e.buttons);
|
| + } else {
|
| + dispatcher.move(e);
|
| + }
|
| + }
|
| + }
|
| + },
|
| + mouseup: function(inEvent) {
|
| + if (!this.isEventSimulatedFromTouch(inEvent)) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.relatedTarget = scope.findTarget(inEvent);
|
| + e.target = pointermap.get(this.POINTER_ID);
|
| + dispatcher.up(e);
|
| + this.cleanupMouse(e.buttons);
|
| + }
|
| + },
|
| + cleanupMouse: function(buttons) {
|
| + if (buttons === 0) {
|
| + pointermap.delete(this.POINTER_ID);
|
| + }
|
| + }
|
| + };
|
| +
|
| + scope.mouseEvents = mouseEvents;
|
| +})(window.PolymerGestures);
|
| +
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var allShadows = scope.targetFinding.allShadows.bind(scope.targetFinding);
|
| + var pointermap = dispatcher.pointermap;
|
| + var touchMap = Array.prototype.map.call.bind(Array.prototype.map);
|
| + // This should be long enough to ignore compat mouse events made by touch
|
| + var DEDUP_TIMEOUT = 2500;
|
| + var DEDUP_DIST = 25;
|
| + var CLICK_COUNT_TIMEOUT = 200;
|
| + var HYSTERESIS = 20;
|
| + var ATTRIB = 'touch-action';
|
| + // TODO(dfreedm): disable until http://crbug.com/399765 is resolved
|
| + // var HAS_TOUCH_ACTION = ATTRIB in document.head.style;
|
| + var HAS_TOUCH_ACTION = false;
|
| +
|
| + // handler block for native touch events
|
| + var touchEvents = {
|
| + IS_IOS: false,
|
| + events: [
|
| + 'touchstart',
|
| + 'touchmove',
|
| + 'touchend',
|
| + 'touchcancel'
|
| + ],
|
| + exposes: [
|
| + 'down',
|
| + 'up',
|
| + 'move'
|
| + ],
|
| + register: function(target, initial) {
|
| + if (this.IS_IOS ? initial : !initial) {
|
| + dispatcher.listen(target, this.events);
|
| + }
|
| + },
|
| + unregister: function(target) {
|
| + if (!this.IS_IOS) {
|
| + dispatcher.unlisten(target, this.events);
|
| + }
|
| + },
|
| + scrollTypes: {
|
| + EMITTER: 'none',
|
| + XSCROLLER: 'pan-x',
|
| + YSCROLLER: 'pan-y',
|
| + },
|
| + touchActionToScrollType: function(touchAction) {
|
| + var t = touchAction;
|
| + var st = this.scrollTypes;
|
| + if (t === st.EMITTER) {
|
| + return 'none';
|
| + } else if (t === st.XSCROLLER) {
|
| + return 'X';
|
| + } else if (t === st.YSCROLLER) {
|
| + return 'Y';
|
| + } else {
|
| + return 'XY';
|
| + }
|
| + },
|
| + POINTER_TYPE: 'touch',
|
| + firstTouch: null,
|
| + isPrimaryTouch: function(inTouch) {
|
| + return this.firstTouch === inTouch.identifier;
|
| + },
|
| + setPrimaryTouch: function(inTouch) {
|
| + // set primary touch if there no pointers, or the only pointer is the mouse
|
| + if (pointermap.pointers() === 0 || (pointermap.pointers() === 1 && pointermap.has(1))) {
|
| + this.firstTouch = inTouch.identifier;
|
| + this.firstXY = {X: inTouch.clientX, Y: inTouch.clientY};
|
| + this.firstTarget = inTouch.target;
|
| + this.scrolling = null;
|
| + this.cancelResetClickCount();
|
| + }
|
| + },
|
| + removePrimaryPointer: function(inPointer) {
|
| + if (inPointer.isPrimary) {
|
| + this.firstTouch = null;
|
| + this.firstXY = null;
|
| + this.resetClickCount();
|
| + }
|
| + },
|
| + clickCount: 0,
|
| + resetId: null,
|
| + resetClickCount: function() {
|
| + var fn = function() {
|
| + this.clickCount = 0;
|
| + this.resetId = null;
|
| + }.bind(this);
|
| + this.resetId = setTimeout(fn, CLICK_COUNT_TIMEOUT);
|
| + },
|
| + cancelResetClickCount: function() {
|
| + if (this.resetId) {
|
| + clearTimeout(this.resetId);
|
| + }
|
| + },
|
| + typeToButtons: function(type) {
|
| + var ret = 0;
|
| + if (type === 'touchstart' || type === 'touchmove') {
|
| + ret = 1;
|
| + }
|
| + return ret;
|
| + },
|
| + findTarget: function(touch, id) {
|
| + if (this.currentTouchEvent.type === 'touchstart') {
|
| + if (this.isPrimaryTouch(touch)) {
|
| + var fastPath = {
|
| + clientX: touch.clientX,
|
| + clientY: touch.clientY,
|
| + path: this.currentTouchEvent.path,
|
| + target: this.currentTouchEvent.target
|
| + };
|
| + return scope.findTarget(fastPath);
|
| + } else {
|
| + return scope.findTarget(touch);
|
| + }
|
| + }
|
| + // reuse target we found in touchstart
|
| + return pointermap.get(id);
|
| + },
|
| + touchToPointer: function(inTouch) {
|
| + var cte = this.currentTouchEvent;
|
| + var e = dispatcher.cloneEvent(inTouch);
|
| + // Spec specifies that pointerId 1 is reserved for Mouse.
|
| + // Touch identifiers can start at 0.
|
| + // Add 2 to the touch identifier for compatibility.
|
| + var id = e.pointerId = inTouch.identifier + 2;
|
| + e.target = this.findTarget(inTouch, id);
|
| + e.bubbles = true;
|
| + e.cancelable = true;
|
| + e.detail = this.clickCount;
|
| + e.buttons = this.typeToButtons(cte.type);
|
| + e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0;
|
| + e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0;
|
| + e.pressure = inTouch.webkitForce || inTouch.force || 0.5;
|
| + e.isPrimary = this.isPrimaryTouch(inTouch);
|
| + e.pointerType = this.POINTER_TYPE;
|
| + e._source = 'touch';
|
| + // forward touch preventDefaults
|
| + var self = this;
|
| + e.preventDefault = function() {
|
| + self.scrolling = false;
|
| + self.firstXY = null;
|
| + cte.preventDefault();
|
| + };
|
| + return e;
|
| + },
|
| + processTouches: function(inEvent, inFunction) {
|
| + var tl = inEvent.changedTouches;
|
| + this.currentTouchEvent = inEvent;
|
| + for (var i = 0, t, p; i < tl.length; i++) {
|
| + t = tl[i];
|
| + p = this.touchToPointer(t);
|
| + if (inEvent.type === 'touchstart') {
|
| + pointermap.set(p.pointerId, p.target);
|
| + }
|
| + if (pointermap.has(p.pointerId)) {
|
| + inFunction.call(this, p);
|
| + }
|
| + if (inEvent.type === 'touchend' || inEvent._cancel) {
|
| + this.cleanUpPointer(p);
|
| + }
|
| + }
|
| + },
|
| + // For single axis scrollers, determines whether the element should emit
|
| + // pointer events or behave as a scroller
|
| + shouldScroll: function(inEvent) {
|
| + if (this.firstXY) {
|
| + var ret;
|
| + var touchAction = scope.targetFinding.findTouchAction(inEvent);
|
| + var scrollAxis = this.touchActionToScrollType(touchAction);
|
| + if (scrollAxis === 'none') {
|
| + // this element is a touch-action: none, should never scroll
|
| + ret = false;
|
| + } else if (scrollAxis === 'XY') {
|
| + // this element should always scroll
|
| + ret = true;
|
| + } else {
|
| + var t = inEvent.changedTouches[0];
|
| + // check the intended scroll axis, and other axis
|
| + var a = scrollAxis;
|
| + var oa = scrollAxis === 'Y' ? 'X' : 'Y';
|
| + var da = Math.abs(t['client' + a] - this.firstXY[a]);
|
| + var doa = Math.abs(t['client' + oa] - this.firstXY[oa]);
|
| + // if delta in the scroll axis > delta other axis, scroll instead of
|
| + // making events
|
| + ret = da >= doa;
|
| + }
|
| + return ret;
|
| + }
|
| + },
|
| + findTouch: function(inTL, inId) {
|
| + for (var i = 0, l = inTL.length, t; i < l && (t = inTL[i]); i++) {
|
| + if (t.identifier === inId) {
|
| + return true;
|
| + }
|
| + }
|
| + },
|
| + // In some instances, a touchstart can happen without a touchend. This
|
| + // leaves the pointermap in a broken state.
|
| + // Therefore, on every touchstart, we remove the touches that did not fire a
|
| + // touchend event.
|
| + // To keep state globally consistent, we fire a
|
| + // pointercancel for this "abandoned" touch
|
| + vacuumTouches: function(inEvent) {
|
| + var tl = inEvent.touches;
|
| + // pointermap.pointers() should be < tl.length here, as the touchstart has not
|
| + // been processed yet.
|
| + if (pointermap.pointers() >= tl.length) {
|
| + var d = [];
|
| + pointermap.forEach(function(value, key) {
|
| + // Never remove pointerId == 1, which is mouse.
|
| + // Touch identifiers are 2 smaller than their pointerId, which is the
|
| + // index in pointermap.
|
| + if (key !== 1 && !this.findTouch(tl, key - 2)) {
|
| + var p = value;
|
| + d.push(p);
|
| + }
|
| + }, this);
|
| + d.forEach(function(p) {
|
| + this.cancel(p);
|
| + pointermap.delete(p.pointerId);
|
| + });
|
| + }
|
| + },
|
| + touchstart: function(inEvent) {
|
| + this.vacuumTouches(inEvent);
|
| + this.setPrimaryTouch(inEvent.changedTouches[0]);
|
| + this.dedupSynthMouse(inEvent);
|
| + if (!this.scrolling) {
|
| + this.clickCount++;
|
| + this.processTouches(inEvent, this.down);
|
| + }
|
| + },
|
| + down: function(inPointer) {
|
| + dispatcher.down(inPointer);
|
| + },
|
| + touchmove: function(inEvent) {
|
| + if (HAS_TOUCH_ACTION) {
|
| + // touchevent.cancelable == false is sent when the page is scrolling under native Touch Action in Chrome 36
|
| + // https://groups.google.com/a/chromium.org/d/msg/input-dev/wHnyukcYBcA/b9kmtwM1jJQJ
|
| + if (inEvent.cancelable) {
|
| + this.processTouches(inEvent, this.move);
|
| + }
|
| + } else {
|
| + if (!this.scrolling) {
|
| + if (this.scrolling === null && this.shouldScroll(inEvent)) {
|
| + this.scrolling = true;
|
| + } else {
|
| + this.scrolling = false;
|
| + inEvent.preventDefault();
|
| + this.processTouches(inEvent, this.move);
|
| + }
|
| + } else if (this.firstXY) {
|
| + var t = inEvent.changedTouches[0];
|
| + var dx = t.clientX - this.firstXY.X;
|
| + var dy = t.clientY - this.firstXY.Y;
|
| + var dd = Math.sqrt(dx * dx + dy * dy);
|
| + if (dd >= HYSTERESIS) {
|
| + this.touchcancel(inEvent);
|
| + this.scrolling = true;
|
| + this.firstXY = null;
|
| + }
|
| + }
|
| + }
|
| + },
|
| + move: function(inPointer) {
|
| + dispatcher.move(inPointer);
|
| + },
|
| + touchend: function(inEvent) {
|
| + this.dedupSynthMouse(inEvent);
|
| + this.processTouches(inEvent, this.up);
|
| + },
|
| + up: function(inPointer) {
|
| + inPointer.relatedTarget = scope.findTarget(inPointer);
|
| + dispatcher.up(inPointer);
|
| + },
|
| + cancel: function(inPointer) {
|
| + dispatcher.cancel(inPointer);
|
| + },
|
| + touchcancel: function(inEvent) {
|
| + inEvent._cancel = true;
|
| + this.processTouches(inEvent, this.cancel);
|
| + },
|
| + cleanUpPointer: function(inPointer) {
|
| + pointermap['delete'](inPointer.pointerId);
|
| + this.removePrimaryPointer(inPointer);
|
| + },
|
| + // prevent synth mouse events from creating pointer events
|
| + dedupSynthMouse: function(inEvent) {
|
| + var lts = scope.mouseEvents.lastTouches;
|
| + var t = inEvent.changedTouches[0];
|
| + // only the primary finger will synth mouse events
|
| + if (this.isPrimaryTouch(t)) {
|
| + // remember x/y of last touch
|
| + var lt = {x: t.clientX, y: t.clientY};
|
| + lts.push(lt);
|
| + var fn = (function(lts, lt){
|
| + var i = lts.indexOf(lt);
|
| + if (i > -1) {
|
| + lts.splice(i, 1);
|
| + }
|
| + }).bind(null, lts, lt);
|
| + setTimeout(fn, DEDUP_TIMEOUT);
|
| + }
|
| + }
|
| + };
|
| +
|
| + // prevent "ghost clicks" that come from elements that were removed in a touch handler
|
| + var STOP_PROP_FN = Event.prototype.stopImmediatePropagation || Event.prototype.stopPropagation;
|
| + document.addEventListener('click', function(ev) {
|
| + var x = ev.clientX, y = ev.clientY;
|
| + // check if a click is within DEDUP_DIST px radius of the touchstart
|
| + var closeTo = function(touch) {
|
| + var dx = Math.abs(x - touch.x), dy = Math.abs(y - touch.y);
|
| + return (dx <= DEDUP_DIST && dy <= DEDUP_DIST);
|
| + };
|
| + // if click coordinates are close to touch coordinates, assume the click came from a touch
|
| + var wasTouched = scope.mouseEvents.lastTouches.some(closeTo);
|
| + // if the click came from touch, and the touchstart target is not in the path of the click event,
|
| + // then the touchstart target was probably removed, and the click should be "busted"
|
| + var path = scope.targetFinding.path(ev);
|
| + if (wasTouched) {
|
| + for (var i = 0; i < path.length; i++) {
|
| + if (path[i] === touchEvents.firstTarget) {
|
| + return;
|
| + }
|
| + }
|
| + ev.preventDefault();
|
| + STOP_PROP_FN.call(ev);
|
| + }
|
| + }, true);
|
| +
|
| + scope.touchEvents = touchEvents;
|
| +})(window.PolymerGestures);
|
| +
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var pointermap = dispatcher.pointermap;
|
| + var HAS_BITMAP_TYPE = window.MSPointerEvent && typeof window.MSPointerEvent.MSPOINTER_TYPE_MOUSE === 'number';
|
| + var msEvents = {
|
| + events: [
|
| + 'MSPointerDown',
|
| + 'MSPointerMove',
|
| + 'MSPointerUp',
|
| + 'MSPointerCancel',
|
| + ],
|
| + register: function(target) {
|
| + dispatcher.listen(target, this.events);
|
| + },
|
| + unregister: function(target) {
|
| + if (target === document) {
|
| + return;
|
| + }
|
| + dispatcher.unlisten(target, this.events);
|
| + },
|
| + POINTER_TYPES: [
|
| + '',
|
| + 'unavailable',
|
| + 'touch',
|
| + 'pen',
|
| + 'mouse'
|
| + ],
|
| + prepareEvent: function(inEvent) {
|
| + var e = inEvent;
|
| + e = dispatcher.cloneEvent(inEvent);
|
| + if (HAS_BITMAP_TYPE) {
|
| + e.pointerType = this.POINTER_TYPES[inEvent.pointerType];
|
| + }
|
| + e._source = 'ms';
|
| + return e;
|
| + },
|
| + cleanup: function(id) {
|
| + pointermap['delete'](id);
|
| + },
|
| + MSPointerDown: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = scope.findTarget(inEvent);
|
| + pointermap.set(inEvent.pointerId, e.target);
|
| + dispatcher.down(e);
|
| + },
|
| + MSPointerMove: function(inEvent) {
|
| + var target = pointermap.get(inEvent.pointerId);
|
| + if (target) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = target;
|
| + dispatcher.move(e);
|
| + }
|
| + },
|
| + MSPointerUp: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.relatedTarget = scope.findTarget(inEvent);
|
| + e.target = pointermap.get(e.pointerId);
|
| + dispatcher.up(e);
|
| + this.cleanup(inEvent.pointerId);
|
| + },
|
| + MSPointerCancel: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.relatedTarget = scope.findTarget(inEvent);
|
| + e.target = pointermap.get(e.pointerId);
|
| + dispatcher.cancel(e);
|
| + this.cleanup(inEvent.pointerId);
|
| + }
|
| + };
|
| +
|
| + scope.msEvents = msEvents;
|
| +})(window.PolymerGestures);
|
| +
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var pointermap = dispatcher.pointermap;
|
| + var pointerEvents = {
|
| + events: [
|
| + 'pointerdown',
|
| + 'pointermove',
|
| + 'pointerup',
|
| + 'pointercancel'
|
| + ],
|
| + prepareEvent: function(inEvent) {
|
| + var e = dispatcher.cloneEvent(inEvent);
|
| + e._source = 'pointer';
|
| + return e;
|
| + },
|
| + register: function(target) {
|
| + dispatcher.listen(target, this.events);
|
| + },
|
| + unregister: function(target) {
|
| + if (target === document) {
|
| + return;
|
| + }
|
| + dispatcher.unlisten(target, this.events);
|
| + },
|
| + cleanup: function(id) {
|
| + pointermap['delete'](id);
|
| + },
|
| + pointerdown: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = scope.findTarget(inEvent);
|
| + pointermap.set(e.pointerId, e.target);
|
| + dispatcher.down(e);
|
| + },
|
| + pointermove: function(inEvent) {
|
| + var target = pointermap.get(inEvent.pointerId);
|
| + if (target) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.target = target;
|
| + dispatcher.move(e);
|
| + }
|
| + },
|
| + pointerup: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.relatedTarget = scope.findTarget(inEvent);
|
| + e.target = pointermap.get(e.pointerId);
|
| + dispatcher.up(e);
|
| + this.cleanup(inEvent.pointerId);
|
| + },
|
| + pointercancel: function(inEvent) {
|
| + var e = this.prepareEvent(inEvent);
|
| + e.relatedTarget = scope.findTarget(inEvent);
|
| + e.target = pointermap.get(e.pointerId);
|
| + dispatcher.cancel(e);
|
| + this.cleanup(inEvent.pointerId);
|
| + }
|
| + };
|
| +
|
| + scope.pointerEvents = pointerEvents;
|
| +})(window.PolymerGestures);
|
| +
|
| +/**
|
| + * This module contains the handlers for native platform events.
|
| + * From here, the dispatcher is called to create unified pointer events.
|
| + * Included are touch events (v1), mouse events, and MSPointerEvents.
|
| + */
|
| +(function(scope) {
|
| +
|
| + var dispatcher = scope.dispatcher;
|
| + var nav = window.navigator;
|
| +
|
| + if (window.PointerEvent) {
|
| + dispatcher.registerSource('pointer', scope.pointerEvents);
|
| + } else if (nav.msPointerEnabled) {
|
| + dispatcher.registerSource('ms', scope.msEvents);
|
| + } else {
|
| + dispatcher.registerSource('mouse', scope.mouseEvents);
|
| + if (window.ontouchstart !== undefined) {
|
| + dispatcher.registerSource('touch', scope.touchEvents);
|
| + }
|
| + }
|
| +
|
| + // Work around iOS bugs https://bugs.webkit.org/show_bug.cgi?id=135628 and https://bugs.webkit.org/show_bug.cgi?id=136506
|
| + var ua = navigator.userAgent;
|
| + var IS_IOS = ua.match(/iPad|iPhone|iPod/) && 'ontouchstart' in window;
|
| +
|
| + dispatcher.IS_IOS = IS_IOS;
|
| + scope.touchEvents.IS_IOS = IS_IOS;
|
| +
|
| + dispatcher.register(document, true);
|
| +})(window.PolymerGestures);
|
| +
|
| +/**
|
| + * This event denotes the beginning of a series of tracking events.
|
| + *
|
| + * @module PointerGestures
|
| + * @submodule Events
|
| + * @class trackstart
|
| + */
|
| +/**
|
| + * Pixels moved in the x direction since trackstart.
|
| + * @type Number
|
| + * @property dx
|
| + */
|
| +/**
|
| + * Pixes moved in the y direction since trackstart.
|
| + * @type Number
|
| + * @property dy
|
| + */
|
| +/**
|
| + * Pixels moved in the x direction since the last track.
|
| + * @type Number
|
| + * @property ddx
|
| + */
|
| +/**
|
| + * Pixles moved in the y direction since the last track.
|
| + * @type Number
|
| + * @property ddy
|
| + */
|
| +/**
|
| + * The clientX position of the track gesture.
|
| + * @type Number
|
| + * @property clientX
|
| + */
|
| +/**
|
| + * The clientY position of the track gesture.
|
| + * @type Number
|
| + * @property clientY
|
| + */
|
| +/**
|
| + * The pageX position of the track gesture.
|
| + * @type Number
|
| + * @property pageX
|
| + */
|
| +/**
|
| + * The pageY position of the track gesture.
|
| + * @type Number
|
| + * @property pageY
|
| + */
|
| +/**
|
| + * The screenX position of the track gesture.
|
| + * @type Number
|
| + * @property screenX
|
| + */
|
| +/**
|
| + * The screenY position of the track gesture.
|
| + * @type Number
|
| + * @property screenY
|
| + */
|
| +/**
|
| + * The last x axis direction of the pointer.
|
| + * @type Number
|
| + * @property xDirection
|
| + */
|
| +/**
|
| + * The last y axis direction of the pointer.
|
| + * @type Number
|
| + * @property yDirection
|
| + */
|
| +/**
|
| + * A shared object between all tracking events.
|
| + * @type Object
|
| + * @property trackInfo
|
| + */
|
| +/**
|
| + * The element currently under the pointer.
|
| + * @type Element
|
| + * @property relatedTarget
|
| + */
|
| +/**
|
| + * The type of pointer that make the track gesture.
|
| + * @type String
|
| + * @property pointerType
|
| + */
|
| +/**
|
| + *
|
| + * This event fires for all pointer movement being tracked.
|
| + *
|
| + * @class track
|
| + * @extends trackstart
|
| + */
|
| +/**
|
| + * This event fires when the pointer is no longer being tracked.
|
| + *
|
| + * @class trackend
|
| + * @extends trackstart
|
| + */
|
| +
|
| + (function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var eventFactory = scope.eventFactory;
|
| + var pointermap = new scope.PointerMap();
|
| + var track = {
|
| + events: [
|
| + 'down',
|
| + 'move',
|
| + 'up',
|
| + ],
|
| + exposes: [
|
| + 'trackstart',
|
| + 'track',
|
| + 'trackx',
|
| + 'tracky',
|
| + 'trackend'
|
| + ],
|
| + defaultActions: {
|
| + 'track': 'none',
|
| + 'trackx': 'pan-y',
|
| + 'tracky': 'pan-x'
|
| + },
|
| + WIGGLE_THRESHOLD: 4,
|
| + clampDir: function(inDelta) {
|
| + return inDelta > 0 ? 1 : -1;
|
| + },
|
| + calcPositionDelta: function(inA, inB) {
|
| + var x = 0, y = 0;
|
| + if (inA && inB) {
|
| + x = inB.pageX - inA.pageX;
|
| + y = inB.pageY - inA.pageY;
|
| + }
|
| + return {x: x, y: y};
|
| + },
|
| + fireTrack: function(inType, inEvent, inTrackingData) {
|
| + var t = inTrackingData;
|
| + var d = this.calcPositionDelta(t.downEvent, inEvent);
|
| + var dd = this.calcPositionDelta(t.lastMoveEvent, inEvent);
|
| + if (dd.x) {
|
| + t.xDirection = this.clampDir(dd.x);
|
| + } else if (inType === 'trackx') {
|
| + return;
|
| + }
|
| + if (dd.y) {
|
| + t.yDirection = this.clampDir(dd.y);
|
| + } else if (inType === 'tracky') {
|
| + return;
|
| + }
|
| + var gestureProto = {
|
| + bubbles: true,
|
| + cancelable: true,
|
| + trackInfo: t.trackInfo,
|
| + relatedTarget: inEvent.relatedTarget,
|
| + pointerType: inEvent.pointerType,
|
| + pointerId: inEvent.pointerId,
|
| + _source: 'track'
|
| + };
|
| + if (inType !== 'tracky') {
|
| + gestureProto.x = inEvent.x;
|
| + gestureProto.dx = d.x;
|
| + gestureProto.ddx = dd.x;
|
| + gestureProto.clientX = inEvent.clientX;
|
| + gestureProto.pageX = inEvent.pageX;
|
| + gestureProto.screenX = inEvent.screenX;
|
| + gestureProto.xDirection = t.xDirection;
|
| + }
|
| + if (inType !== 'trackx') {
|
| + gestureProto.dy = d.y;
|
| + gestureProto.ddy = dd.y;
|
| + gestureProto.y = inEvent.y;
|
| + gestureProto.clientY = inEvent.clientY;
|
| + gestureProto.pageY = inEvent.pageY;
|
| + gestureProto.screenY = inEvent.screenY;
|
| + gestureProto.yDirection = t.yDirection;
|
| + }
|
| + var e = eventFactory.makeGestureEvent(inType, gestureProto);
|
| + t.downTarget.dispatchEvent(e);
|
| + },
|
| + down: function(inEvent) {
|
| + if (inEvent.isPrimary && (inEvent.pointerType === 'mouse' ? inEvent.buttons === 1 : true)) {
|
| + var p = {
|
| + downEvent: inEvent,
|
| + downTarget: inEvent.target,
|
| + trackInfo: {},
|
| + lastMoveEvent: null,
|
| + xDirection: 0,
|
| + yDirection: 0,
|
| + tracking: false
|
| + };
|
| + pointermap.set(inEvent.pointerId, p);
|
| + }
|
| + },
|
| + move: function(inEvent) {
|
| + var p = pointermap.get(inEvent.pointerId);
|
| + if (p) {
|
| + if (!p.tracking) {
|
| + var d = this.calcPositionDelta(p.downEvent, inEvent);
|
| + var move = d.x * d.x + d.y * d.y;
|
| + // start tracking only if finger moves more than WIGGLE_THRESHOLD
|
| + if (move > this.WIGGLE_THRESHOLD) {
|
| + p.tracking = true;
|
| + p.lastMoveEvent = p.downEvent;
|
| + this.fireTrack('trackstart', inEvent, p);
|
| + }
|
| + }
|
| + if (p.tracking) {
|
| + this.fireTrack('track', inEvent, p);
|
| + this.fireTrack('trackx', inEvent, p);
|
| + this.fireTrack('tracky', inEvent, p);
|
| + }
|
| + p.lastMoveEvent = inEvent;
|
| + }
|
| + },
|
| + up: function(inEvent) {
|
| + var p = pointermap.get(inEvent.pointerId);
|
| + if (p) {
|
| + if (p.tracking) {
|
| + this.fireTrack('trackend', inEvent, p);
|
| + }
|
| + pointermap.delete(inEvent.pointerId);
|
| + }
|
| + }
|
| + };
|
| + dispatcher.registerGesture('track', track);
|
| + })(window.PolymerGestures);
|
| +
|
| +/**
|
| + * This event is fired when a pointer is held down for 200ms.
|
| + *
|
| + * @module PointerGestures
|
| + * @submodule Events
|
| + * @class hold
|
| + */
|
| +/**
|
| + * Type of pointer that made the holding event.
|
| + * @type String
|
| + * @property pointerType
|
| + */
|
| +/**
|
| + * Screen X axis position of the held pointer
|
| + * @type Number
|
| + * @property clientX
|
| + */
|
| +/**
|
| + * Screen Y axis position of the held pointer
|
| + * @type Number
|
| + * @property clientY
|
| + */
|
| +/**
|
| + * Type of pointer that made the holding event.
|
| + * @type String
|
| + * @property pointerType
|
| + */
|
| +/**
|
| + * This event is fired every 200ms while a pointer is held down.
|
| + *
|
| + * @class holdpulse
|
| + * @extends hold
|
| + */
|
| +/**
|
| + * Milliseconds pointer has been held down.
|
| + * @type Number
|
| + * @property holdTime
|
| + */
|
| +/**
|
| + * This event is fired when a held pointer is released or moved.
|
| + *
|
| + * @class release
|
| + */
|
| +
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var eventFactory = scope.eventFactory;
|
| + var hold = {
|
| + // wait at least HOLD_DELAY ms between hold and pulse events
|
| + HOLD_DELAY: 200,
|
| + // pointer can move WIGGLE_THRESHOLD pixels before not counting as a hold
|
| + WIGGLE_THRESHOLD: 16,
|
| + events: [
|
| + 'down',
|
| + 'move',
|
| + 'up',
|
| + ],
|
| + exposes: [
|
| + 'hold',
|
| + 'holdpulse',
|
| + 'release'
|
| + ],
|
| + heldPointer: null,
|
| + holdJob: null,
|
| + pulse: function() {
|
| + var hold = Date.now() - this.heldPointer.timeStamp;
|
| + var type = this.held ? 'holdpulse' : 'hold';
|
| + this.fireHold(type, hold);
|
| + this.held = true;
|
| + },
|
| + cancel: function() {
|
| + clearInterval(this.holdJob);
|
| + if (this.held) {
|
| + this.fireHold('release');
|
| + }
|
| + this.held = false;
|
| + this.heldPointer = null;
|
| + this.target = null;
|
| + this.holdJob = null;
|
| + },
|
| + down: function(inEvent) {
|
| + if (inEvent.isPrimary && !this.heldPointer) {
|
| + this.heldPointer = inEvent;
|
| + this.target = inEvent.target;
|
| + this.holdJob = setInterval(this.pulse.bind(this), this.HOLD_DELAY);
|
| + }
|
| + },
|
| + up: function(inEvent) {
|
| + if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
|
| + this.cancel();
|
| + }
|
| + },
|
| + move: function(inEvent) {
|
| + if (this.heldPointer && this.heldPointer.pointerId === inEvent.pointerId) {
|
| + var x = inEvent.clientX - this.heldPointer.clientX;
|
| + var y = inEvent.clientY - this.heldPointer.clientY;
|
| + if ((x * x + y * y) > this.WIGGLE_THRESHOLD) {
|
| + this.cancel();
|
| + }
|
| + }
|
| + },
|
| + fireHold: function(inType, inHoldTime) {
|
| + var p = {
|
| + bubbles: true,
|
| + cancelable: true,
|
| + pointerType: this.heldPointer.pointerType,
|
| + pointerId: this.heldPointer.pointerId,
|
| + x: this.heldPointer.clientX,
|
| + y: this.heldPointer.clientY,
|
| + _source: 'hold'
|
| + };
|
| + if (inHoldTime) {
|
| + p.holdTime = inHoldTime;
|
| + }
|
| + var e = eventFactory.makeGestureEvent(inType, p);
|
| + this.target.dispatchEvent(e);
|
| + }
|
| + };
|
| + dispatcher.registerGesture('hold', hold);
|
| +})(window.PolymerGestures);
|
| +
|
| +/**
|
| + * This event is fired when a pointer quickly goes down and up, and is used to
|
| + * denote activation.
|
| + *
|
| + * Any gesture event can prevent the tap event from being created by calling
|
| + * `event.preventTap`.
|
| + *
|
| + * Any pointer event can prevent the tap by setting the `tapPrevented` property
|
| + * on itself.
|
| + *
|
| + * @module PointerGestures
|
| + * @submodule Events
|
| + * @class tap
|
| + */
|
| +/**
|
| + * X axis position of the tap.
|
| + * @property x
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Y axis position of the tap.
|
| + * @property y
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Type of the pointer that made the tap.
|
| + * @property pointerType
|
| + * @type String
|
| + */
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var eventFactory = scope.eventFactory;
|
| + var pointermap = new scope.PointerMap();
|
| + var tap = {
|
| + events: [
|
| + 'down',
|
| + 'up'
|
| + ],
|
| + exposes: [
|
| + 'tap'
|
| + ],
|
| + down: function(inEvent) {
|
| + if (inEvent.isPrimary && !inEvent.tapPrevented) {
|
| + pointermap.set(inEvent.pointerId, {
|
| + target: inEvent.target,
|
| + buttons: inEvent.buttons,
|
| + x: inEvent.clientX,
|
| + y: inEvent.clientY
|
| + });
|
| + }
|
| + },
|
| + shouldTap: function(e, downState) {
|
| + var tap = true;
|
| + if (e.pointerType === 'mouse') {
|
| + // only allow left click to tap for mouse
|
| + tap = (e.buttons ^ 1) && (downState.buttons & 1);
|
| + }
|
| + return tap && !e.tapPrevented;
|
| + },
|
| + up: function(inEvent) {
|
| + var start = pointermap.get(inEvent.pointerId);
|
| + if (start && this.shouldTap(inEvent, start)) {
|
| + // up.relatedTarget is target currently under finger
|
| + var t = scope.targetFinding.LCA(start.target, inEvent.relatedTarget);
|
| + if (t) {
|
| + var e = eventFactory.makeGestureEvent('tap', {
|
| + bubbles: true,
|
| + cancelable: true,
|
| + x: inEvent.clientX,
|
| + y: inEvent.clientY,
|
| + detail: inEvent.detail,
|
| + pointerType: inEvent.pointerType,
|
| + pointerId: inEvent.pointerId,
|
| + altKey: inEvent.altKey,
|
| + ctrlKey: inEvent.ctrlKey,
|
| + metaKey: inEvent.metaKey,
|
| + shiftKey: inEvent.shiftKey,
|
| + _source: 'tap'
|
| + });
|
| + t.dispatchEvent(e);
|
| + }
|
| + }
|
| + pointermap.delete(inEvent.pointerId);
|
| + }
|
| + };
|
| + // patch eventFactory to remove id from tap's pointermap for preventTap calls
|
| + eventFactory.preventTap = function(e) {
|
| + return function() {
|
| + e.tapPrevented = true;
|
| + pointermap.delete(e.pointerId);
|
| + };
|
| + };
|
| + dispatcher.registerGesture('tap', tap);
|
| +})(window.PolymerGestures);
|
| +
|
| +/*
|
| + * Basic strategy: find the farthest apart points, use as diameter of circle
|
| + * react to size change and rotation of the chord
|
| + */
|
| +
|
| +/**
|
| + * @module pointer-gestures
|
| + * @submodule Events
|
| + * @class pinch
|
| + */
|
| +/**
|
| + * Scale of the pinch zoom gesture
|
| + * @property scale
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Center X position of pointers causing pinch
|
| + * @property centerX
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Center Y position of pointers causing pinch
|
| + * @property centerY
|
| + * @type Number
|
| + */
|
| +
|
| +/**
|
| + * @module pointer-gestures
|
| + * @submodule Events
|
| + * @class rotate
|
| + */
|
| +/**
|
| + * Angle (in degrees) of rotation. Measured from starting positions of pointers.
|
| + * @property angle
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Center X position of pointers causing rotation
|
| + * @property centerX
|
| + * @type Number
|
| + */
|
| +/**
|
| + * Center Y position of pointers causing rotation
|
| + * @property centerY
|
| + * @type Number
|
| + */
|
| +(function(scope) {
|
| + var dispatcher = scope.dispatcher;
|
| + var eventFactory = scope.eventFactory;
|
| + var pointermap = new scope.PointerMap();
|
| + var RAD_TO_DEG = 180 / Math.PI;
|
| + var pinch = {
|
| + events: [
|
| + 'down',
|
| + 'up',
|
| + 'move',
|
| + 'cancel'
|
| + ],
|
| + exposes: [
|
| + 'pinch',
|
| + 'rotate'
|
| + ],
|
| + defaultActions: {
|
| + 'pinch': 'none',
|
| + 'rotate': 'none'
|
| + },
|
| + reference: {},
|
| + down: function(inEvent) {
|
| + pointermap.set(inEvent.pointerId, inEvent);
|
| + if (pointermap.pointers() == 2) {
|
| + var points = this.calcChord();
|
| + var angle = this.calcAngle(points);
|
| + this.reference = {
|
| + angle: angle,
|
| + diameter: points.diameter,
|
| + target: scope.targetFinding.LCA(points.a.target, points.b.target)
|
| + };
|
| + }
|
| + },
|
| + up: function(inEvent) {
|
| + var p = pointermap.get(inEvent.pointerId);
|
| + if (p) {
|
| + pointermap.delete(inEvent.pointerId);
|
| + }
|
| + },
|
| + move: function(inEvent) {
|
| + if (pointermap.has(inEvent.pointerId)) {
|
| + pointermap.set(inEvent.pointerId, inEvent);
|
| + if (pointermap.pointers() > 1) {
|
| + this.calcPinchRotate();
|
| + }
|
| + }
|
| + },
|
| + cancel: function(inEvent) {
|
| + this.up(inEvent);
|
| + },
|
| + firePinch: function(diameter, points) {
|
| + var zoom = diameter / this.reference.diameter;
|
| + var e = eventFactory.makeGestureEvent('pinch', {
|
| + bubbles: true,
|
| + cancelable: true,
|
| + scale: zoom,
|
| + centerX: points.center.x,
|
| + centerY: points.center.y,
|
| + _source: 'pinch'
|
| + });
|
| + this.reference.target.dispatchEvent(e);
|
| + },
|
| + fireRotate: function(angle, points) {
|
| + var diff = Math.round((angle - this.reference.angle) % 360);
|
| + var e = eventFactory.makeGestureEvent('rotate', {
|
| + bubbles: true,
|
| + cancelable: true,
|
| + angle: diff,
|
| + centerX: points.center.x,
|
| + centerY: points.center.y,
|
| + _source: 'pinch'
|
| + });
|
| + this.reference.target.dispatchEvent(e);
|
| + },
|
| + calcPinchRotate: function() {
|
| + var points = this.calcChord();
|
| + var diameter = points.diameter;
|
| + var angle = this.calcAngle(points);
|
| + if (diameter != this.reference.diameter) {
|
| + this.firePinch(diameter, points);
|
| + }
|
| + if (angle != this.reference.angle) {
|
| + this.fireRotate(angle, points);
|
| + }
|
| + },
|
| + calcChord: function() {
|
| + var pointers = [];
|
| + pointermap.forEach(function(p) {
|
| + pointers.push(p);
|
| + });
|
| + var dist = 0;
|
| + // start with at least two pointers
|
| + var points = {a: pointers[0], b: pointers[1]};
|
| + var x, y, d;
|
| + for (var i = 0; i < pointers.length; i++) {
|
| + var a = pointers[i];
|
| + for (var j = i + 1; j < pointers.length; j++) {
|
| + var b = pointers[j];
|
| + x = Math.abs(a.clientX - b.clientX);
|
| + y = Math.abs(a.clientY - b.clientY);
|
| + d = x + y;
|
| + if (d > dist) {
|
| + dist = d;
|
| + points = {a: a, b: b};
|
| + }
|
| + }
|
| + }
|
| + x = Math.abs(points.a.clientX + points.b.clientX) / 2;
|
| + y = Math.abs(points.a.clientY + points.b.clientY) / 2;
|
| + points.center = { x: x, y: y };
|
| + points.diameter = dist;
|
| + return points;
|
| + },
|
| + calcAngle: function(points) {
|
| + var x = points.a.clientX - points.b.clientX;
|
| + var y = points.a.clientY - points.b.clientY;
|
| + return (360 + Math.atan2(y, x) * RAD_TO_DEG) % 360;
|
| + }
|
| + };
|
| + dispatcher.registerGesture('pinch', pinch);
|
| +})(window.PolymerGestures);
|
| +
|
| +(function (global) {
|
| + 'use strict';
|
| +
|
| + var Token,
|
| + TokenName,
|
| + Syntax,
|
| + Messages,
|
| + source,
|
| + index,
|
| + length,
|
| + delegate,
|
| + lookahead,
|
| + state;
|
| +
|
| + Token = {
|
| + BooleanLiteral: 1,
|
| + EOF: 2,
|
| + Identifier: 3,
|
| + Keyword: 4,
|
| + NullLiteral: 5,
|
| + NumericLiteral: 6,
|
| + Punctuator: 7,
|
| + StringLiteral: 8
|
| + };
|
| +
|
| + TokenName = {};
|
| + TokenName[Token.BooleanLiteral] = 'Boolean';
|
| + TokenName[Token.EOF] = '<end>';
|
| + TokenName[Token.Identifier] = 'Identifier';
|
| + TokenName[Token.Keyword] = 'Keyword';
|
| + TokenName[Token.NullLiteral] = 'Null';
|
| + TokenName[Token.NumericLiteral] = 'Numeric';
|
| + TokenName[Token.Punctuator] = 'Punctuator';
|
| + TokenName[Token.StringLiteral] = 'String';
|
| +
|
| + Syntax = {
|
| + ArrayExpression: 'ArrayExpression',
|
| + BinaryExpression: 'BinaryExpression',
|
| + CallExpression: 'CallExpression',
|
| + ConditionalExpression: 'ConditionalExpression',
|
| + EmptyStatement: 'EmptyStatement',
|
| + ExpressionStatement: 'ExpressionStatement',
|
| + Identifier: 'Identifier',
|
| + Literal: 'Literal',
|
| + LabeledStatement: 'LabeledStatement',
|
| + LogicalExpression: 'LogicalExpression',
|
| + MemberExpression: 'MemberExpression',
|
| + ObjectExpression: 'ObjectExpression',
|
| + Program: 'Program',
|
| + Property: 'Property',
|
| + ThisExpression: 'ThisExpression',
|
| + UnaryExpression: 'UnaryExpression'
|
| + };
|
| +
|
| + // Error messages should be identical to V8.
|
| + Messages = {
|
| + UnexpectedToken: 'Unexpected token %0',
|
| + UnknownLabel: 'Undefined label \'%0\'',
|
| + Redeclaration: '%0 \'%1\' has already been declared'
|
| + };
|
| +
|
| + // Ensure the condition is true, otherwise throw an error.
|
| + // This is only to have a better contract semantic, i.e. another safety net
|
| + // to catch a logic error. The condition shall be fulfilled in normal case.
|
| + // Do NOT use this to enforce a certain condition on any user input.
|
| +
|
| + function assert(condition, message) {
|
| + if (!condition) {
|
| + throw new Error('ASSERT: ' + message);
|
| + }
|
| + }
|
| +
|
| + function isDecimalDigit(ch) {
|
| + return (ch >= 48 && ch <= 57); // 0..9
|
| + }
|
| +
|
| +
|
| + // 7.2 White Space
|
| +
|
| + function isWhiteSpace(ch) {
|
| + return (ch === 32) || // space
|
| + (ch === 9) || // tab
|
| + (ch === 0xB) ||
|
| + (ch === 0xC) ||
|
| + (ch === 0xA0) ||
|
| + (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0);
|
| + }
|
| +
|
| + // 7.3 Line Terminators
|
| +
|
| + function isLineTerminator(ch) {
|
| + return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029);
|
| + }
|
| +
|
| + // 7.6 Identifier Names and Identifiers
|
| +
|
| + function isIdentifierStart(ch) {
|
| + return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
|
| + (ch >= 65 && ch <= 90) || // A..Z
|
| + (ch >= 97 && ch <= 122); // a..z
|
| + }
|
| +
|
| + function isIdentifierPart(ch) {
|
| + return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore)
|
| + (ch >= 65 && ch <= 90) || // A..Z
|
| + (ch >= 97 && ch <= 122) || // a..z
|
| + (ch >= 48 && ch <= 57); // 0..9
|
| + }
|
| +
|
| + // 7.6.1.1 Keywords
|
| +
|
| + function isKeyword(id) {
|
| + return (id === 'this')
|
| + }
|
| +
|
| + // 7.4 Comments
|
| +
|
| + function skipWhitespace() {
|
| + while (index < length && isWhiteSpace(source.charCodeAt(index))) {
|
| + ++index;
|
| + }
|
| + }
|
| +
|
| + function getIdentifier() {
|
| + var start, ch;
|
| +
|
| + start = index++;
|
| + while (index < length) {
|
| + ch = source.charCodeAt(index);
|
| + if (isIdentifierPart(ch)) {
|
| + ++index;
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return source.slice(start, index);
|
| + }
|
| +
|
| + function scanIdentifier() {
|
| + var start, id, type;
|
| +
|
| + start = index;
|
| +
|
| + id = getIdentifier();
|
| +
|
| + // There is no keyword or literal with only one character.
|
| + // Thus, it must be an identifier.
|
| + if (id.length === 1) {
|
| + type = Token.Identifier;
|
| + } else if (isKeyword(id)) {
|
| + type = Token.Keyword;
|
| + } else if (id === 'null') {
|
| + type = Token.NullLiteral;
|
| + } else if (id === 'true' || id === 'false') {
|
| + type = Token.BooleanLiteral;
|
| + } else {
|
| + type = Token.Identifier;
|
| + }
|
| +
|
| + return {
|
| + type: type,
|
| + value: id,
|
| + range: [start, index]
|
| + };
|
| + }
|
| +
|
| +
|
| + // 7.7 Punctuators
|
| +
|
| + function scanPunctuator() {
|
| + var start = index,
|
| + code = source.charCodeAt(index),
|
| + code2,
|
| + ch1 = source[index],
|
| + ch2;
|
| +
|
| + switch (code) {
|
| +
|
| + // Check for most common single-character punctuators.
|
| + case 46: // . dot
|
| + case 40: // ( open bracket
|
| + case 41: // ) close bracket
|
| + case 59: // ; semicolon
|
| + case 44: // , comma
|
| + case 123: // { open curly brace
|
| + case 125: // } close curly brace
|
| + case 91: // [
|
| + case 93: // ]
|
| + case 58: // :
|
| + case 63: // ?
|
| + ++index;
|
| + return {
|
| + type: Token.Punctuator,
|
| + value: String.fromCharCode(code),
|
| + range: [start, index]
|
| + };
|
| +
|
| + default:
|
| + code2 = source.charCodeAt(index + 1);
|
| +
|
| + // '=' (char #61) marks an assignment or comparison operator.
|
| + if (code2 === 61) {
|
| + switch (code) {
|
| + case 37: // %
|
| + case 38: // &
|
| + case 42: // *:
|
| + case 43: // +
|
| + case 45: // -
|
| + case 47: // /
|
| + case 60: // <
|
| + case 62: // >
|
| + case 124: // |
|
| + index += 2;
|
| + return {
|
| + type: Token.Punctuator,
|
| + value: String.fromCharCode(code) + String.fromCharCode(code2),
|
| + range: [start, index]
|
| + };
|
| +
|
| + case 33: // !
|
| + case 61: // =
|
| + index += 2;
|
| +
|
| + // !== and ===
|
| + if (source.charCodeAt(index) === 61) {
|
| + ++index;
|
| + }
|
| + return {
|
| + type: Token.Punctuator,
|
| + value: source.slice(start, index),
|
| + range: [start, index]
|
| + };
|
| + default:
|
| + break;
|
| + }
|
| + }
|
| + break;
|
| + }
|
| +
|
| + // Peek more characters.
|
| +
|
| + ch2 = source[index + 1];
|
| +
|
| + // Other 2-character punctuators: && ||
|
| +
|
| + if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) {
|
| + index += 2;
|
| + return {
|
| + type: Token.Punctuator,
|
| + value: ch1 + ch2,
|
| + range: [start, index]
|
| + };
|
| + }
|
| +
|
| + if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
|
| + ++index;
|
| + return {
|
| + type: Token.Punctuator,
|
| + value: ch1,
|
| + range: [start, index]
|
| + };
|
| + }
|
| +
|
| + throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
|
| + }
|
| +
|
| + // 7.8.3 Numeric Literals
|
| + function scanNumericLiteral() {
|
| + var number, start, ch;
|
| +
|
| + ch = source[index];
|
| + assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
|
| + 'Numeric literal must start with a decimal digit or a decimal point');
|
| +
|
| + start = index;
|
| + number = '';
|
| + if (ch !== '.') {
|
| + number = source[index++];
|
| + ch = source[index];
|
| +
|
| + // Hex number starts with '0x'.
|
| + // Octal number starts with '0'.
|
| + if (number === '0') {
|
| + // decimal number starts with '0' such as '09' is illegal.
|
| + if (ch && isDecimalDigit(ch.charCodeAt(0))) {
|
| + throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
|
| + }
|
| + }
|
| +
|
| + while (isDecimalDigit(source.charCodeAt(index))) {
|
| + number += source[index++];
|
| + }
|
| + ch = source[index];
|
| + }
|
| +
|
| + if (ch === '.') {
|
| + number += source[index++];
|
| + while (isDecimalDigit(source.charCodeAt(index))) {
|
| + number += source[index++];
|
| + }
|
| + ch = source[index];
|
| + }
|
| +
|
| + if (ch === 'e' || ch === 'E') {
|
| + number += source[index++];
|
| +
|
| + ch = source[index];
|
| + if (ch === '+' || ch === '-') {
|
| + number += source[index++];
|
| + }
|
| + if (isDecimalDigit(source.charCodeAt(index))) {
|
| + while (isDecimalDigit(source.charCodeAt(index))) {
|
| + number += source[index++];
|
| + }
|
| + } else {
|
| + throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
|
| + }
|
| + }
|
| +
|
| + if (isIdentifierStart(source.charCodeAt(index))) {
|
| + throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
|
| + }
|
| +
|
| + return {
|
| + type: Token.NumericLiteral,
|
| + value: parseFloat(number),
|
| + range: [start, index]
|
| + };
|
| + }
|
| +
|
| + // 7.8.4 String Literals
|
| +
|
| + function scanStringLiteral() {
|
| + var str = '', quote, start, ch, octal = false;
|
| +
|
| + quote = source[index];
|
| + assert((quote === '\'' || quote === '"'),
|
| + 'String literal must starts with a quote');
|
| +
|
| + start = index;
|
| + ++index;
|
| +
|
| + while (index < length) {
|
| + ch = source[index++];
|
| +
|
| + if (ch === quote) {
|
| + quote = '';
|
| + break;
|
| + } else if (ch === '\\') {
|
| + ch = source[index++];
|
| + if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
|
| + switch (ch) {
|
| + case 'n':
|
| + str += '\n';
|
| + break;
|
| + case 'r':
|
| + str += '\r';
|
| + break;
|
| + case 't':
|
| + str += '\t';
|
| + break;
|
| + case 'b':
|
| + str += '\b';
|
| + break;
|
| + case 'f':
|
| + str += '\f';
|
| + break;
|
| + case 'v':
|
| + str += '\x0B';
|
| + break;
|
| +
|
| + default:
|
| + str += ch;
|
| + break;
|
| + }
|
| + } else {
|
| + if (ch === '\r' && source[index] === '\n') {
|
| + ++index;
|
| + }
|
| + }
|
| + } else if (isLineTerminator(ch.charCodeAt(0))) {
|
| + break;
|
| + } else {
|
| + str += ch;
|
| + }
|
| + }
|
| +
|
| + if (quote !== '') {
|
| + throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
|
| + }
|
| +
|
| + return {
|
| + type: Token.StringLiteral,
|
| + value: str,
|
| + octal: octal,
|
| + range: [start, index]
|
| + };
|
| + }
|
| +
|
| + function isIdentifierName(token) {
|
| + return token.type === Token.Identifier ||
|
| + token.type === Token.Keyword ||
|
| + token.type === Token.BooleanLiteral ||
|
| + token.type === Token.NullLiteral;
|
| + }
|
| +
|
| + function advance() {
|
| + var ch;
|
| +
|
| + skipWhitespace();
|
| +
|
| + if (index >= length) {
|
| + return {
|
| + type: Token.EOF,
|
| + range: [index, index]
|
| + };
|
| + }
|
| +
|
| + ch = source.charCodeAt(index);
|
| +
|
| + // Very common: ( and ) and ;
|
| + if (ch === 40 || ch === 41 || ch === 58) {
|
| + return scanPunctuator();
|
| + }
|
| +
|
| + // String literal starts with single quote (#39) or double quote (#34).
|
| + if (ch === 39 || ch === 34) {
|
| + return scanStringLiteral();
|
| + }
|
| +
|
| + if (isIdentifierStart(ch)) {
|
| + return scanIdentifier();
|
| + }
|
| +
|
| + // Dot (.) char #46 can also start a floating-point number, hence the need
|
| + // to check the next character.
|
| + if (ch === 46) {
|
| + if (isDecimalDigit(source.charCodeAt(index + 1))) {
|
| + return scanNumericLiteral();
|
| + }
|
| + return scanPunctuator();
|
| + }
|
| +
|
| + if (isDecimalDigit(ch)) {
|
| + return scanNumericLiteral();
|
| + }
|
| +
|
| + return scanPunctuator();
|
| + }
|
| +
|
| + function lex() {
|
| + var token;
|
| +
|
| + token = lookahead;
|
| + index = token.range[1];
|
| +
|
| + lookahead = advance();
|
| +
|
| + index = token.range[1];
|
| +
|
| + return token;
|
| + }
|
| +
|
| + function peek() {
|
| + var pos;
|
| +
|
| + pos = index;
|
| + lookahead = advance();
|
| + index = pos;
|
| + }
|
| +
|
| + // Throw an exception
|
| +
|
| + function throwError(token, messageFormat) {
|
| + var error,
|
| + args = Array.prototype.slice.call(arguments, 2),
|
| + msg = messageFormat.replace(
|
| + /%(\d)/g,
|
| + function (whole, index) {
|
| + assert(index < args.length, 'Message reference must be in range');
|
| + return args[index];
|
| + }
|
| + );
|
| +
|
| + error = new Error(msg);
|
| + error.index = index;
|
| + error.description = msg;
|
| + throw error;
|
| + }
|
| +
|
| + // Throw an exception because of the token.
|
| +
|
| + function throwUnexpected(token) {
|
| + throwError(token, Messages.UnexpectedToken, token.value);
|
| + }
|
| +
|
| + // Expect the next token to match the specified punctuator.
|
| + // If not, an exception will be thrown.
|
| +
|
| + function expect(value) {
|
| + var token = lex();
|
| + if (token.type !== Token.Punctuator || token.value !== value) {
|
| + throwUnexpected(token);
|
| + }
|
| + }
|
| +
|
| + // Return true if the next token matches the specified punctuator.
|
| +
|
| + function match(value) {
|
| + return lookahead.type === Token.Punctuator && lookahead.value === value;
|
| + }
|
| +
|
| + // Return true if the next token matches the specified keyword
|
| +
|
| + function matchKeyword(keyword) {
|
| + return lookahead.type === Token.Keyword && lookahead.value === keyword;
|
| + }
|
| +
|
| + function consumeSemicolon() {
|
| + // Catch the very common case first: immediately a semicolon (char #59).
|
| + if (source.charCodeAt(index) === 59) {
|
| + lex();
|
| + return;
|
| + }
|
| +
|
| + skipWhitespace();
|
| +
|
| + if (match(';')) {
|
| + lex();
|
| + return;
|
| + }
|
| +
|
| + if (lookahead.type !== Token.EOF && !match('}')) {
|
| + throwUnexpected(lookahead);
|
| + }
|
| + }
|
| +
|
| + // 11.1.4 Array Initialiser
|
| +
|
| + function parseArrayInitialiser() {
|
| + var elements = [];
|
| +
|
| + expect('[');
|
| +
|
| + while (!match(']')) {
|
| + if (match(',')) {
|
| + lex();
|
| + elements.push(null);
|
| + } else {
|
| + elements.push(parseExpression());
|
| +
|
| + if (!match(']')) {
|
| + expect(',');
|
| + }
|
| + }
|
| + }
|
| +
|
| + expect(']');
|
| +
|
| + return delegate.createArrayExpression(elements);
|
| + }
|
| +
|
| + // 11.1.5 Object Initialiser
|
| +
|
| + function parseObjectPropertyKey() {
|
| + var token;
|
| +
|
| + skipWhitespace();
|
| + token = lex();
|
| +
|
| + // Note: This function is called only from parseObjectProperty(), where
|
| + // EOF and Punctuator tokens are already filtered out.
|
| + if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
|
| + return delegate.createLiteral(token);
|
| + }
|
| +
|
| + return delegate.createIdentifier(token.value);
|
| + }
|
| +
|
| + function parseObjectProperty() {
|
| + var token, key;
|
| +
|
| + token = lookahead;
|
| + skipWhitespace();
|
| +
|
| + if (token.type === Token.EOF || token.type === Token.Punctuator) {
|
| + throwUnexpected(token);
|
| + }
|
| +
|
| + key = parseObjectPropertyKey();
|
| + expect(':');
|
| + return delegate.createProperty('init', key, parseExpression());
|
| + }
|
| +
|
| + function parseObjectInitialiser() {
|
| + var properties = [];
|
| +
|
| + expect('{');
|
| +
|
| + while (!match('}')) {
|
| + properties.push(parseObjectProperty());
|
| +
|
| + if (!match('}')) {
|
| + expect(',');
|
| + }
|
| + }
|
| +
|
| + expect('}');
|
| +
|
| + return delegate.createObjectExpression(properties);
|
| + }
|
| +
|
| + // 11.1.6 The Grouping Operator
|
| +
|
| + function parseGroupExpression() {
|
| + var expr;
|
| +
|
| + expect('(');
|
| +
|
| + expr = parseExpression();
|
| +
|
| + expect(')');
|
| +
|
| + return expr;
|
| + }
|
| +
|
| +
|
| + // 11.1 Primary Expressions
|
| +
|
| + function parsePrimaryExpression() {
|
| + var type, token, expr;
|
| +
|
| + if (match('(')) {
|
| + return parseGroupExpression();
|
| + }
|
| +
|
| + type = lookahead.type;
|
| +
|
| + if (type === Token.Identifier) {
|
| + expr = delegate.createIdentifier(lex().value);
|
| + } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
|
| + expr = delegate.createLiteral(lex());
|
| + } else if (type === Token.Keyword) {
|
| + if (matchKeyword('this')) {
|
| + lex();
|
| + expr = delegate.createThisExpression();
|
| + }
|
| + } else if (type === Token.BooleanLiteral) {
|
| + token = lex();
|
| + token.value = (token.value === 'true');
|
| + expr = delegate.createLiteral(token);
|
| + } else if (type === Token.NullLiteral) {
|
| + token = lex();
|
| + token.value = null;
|
| + expr = delegate.createLiteral(token);
|
| + } else if (match('[')) {
|
| + expr = parseArrayInitialiser();
|
| + } else if (match('{')) {
|
| + expr = parseObjectInitialiser();
|
| + }
|
| +
|
| + if (expr) {
|
| + return expr;
|
| + }
|
| +
|
| + throwUnexpected(lex());
|
| + }
|
| +
|
| + // 11.2 Left-Hand-Side Expressions
|
| +
|
| + function parseArguments() {
|
| + var args = [];
|
| +
|
| + expect('(');
|
| +
|
| + if (!match(')')) {
|
| + while (index < length) {
|
| + args.push(parseExpression());
|
| + if (match(')')) {
|
| + break;
|
| + }
|
| + expect(',');
|
| + }
|
| + }
|
| +
|
| + expect(')');
|
| +
|
| + return args;
|
| + }
|
| +
|
| + function parseNonComputedProperty() {
|
| + var token;
|
| +
|
| + token = lex();
|
| +
|
| + if (!isIdentifierName(token)) {
|
| + throwUnexpected(token);
|
| + }
|
| +
|
| + return delegate.createIdentifier(token.value);
|
| + }
|
| +
|
| + function parseNonComputedMember() {
|
| + expect('.');
|
| +
|
| + return parseNonComputedProperty();
|
| + }
|
| +
|
| + function parseComputedMember() {
|
| + var expr;
|
| +
|
| + expect('[');
|
| +
|
| + expr = parseExpression();
|
| +
|
| + expect(']');
|
| +
|
| + return expr;
|
| + }
|
| +
|
| + function parseLeftHandSideExpression() {
|
| + var expr, args, property;
|
| +
|
| + expr = parsePrimaryExpression();
|
| +
|
| + while (true) {
|
| + if (match('[')) {
|
| + property = parseComputedMember();
|
| + expr = delegate.createMemberExpression('[', expr, property);
|
| + } else if (match('.')) {
|
| + property = parseNonComputedMember();
|
| + expr = delegate.createMemberExpression('.', expr, property);
|
| + } else if (match('(')) {
|
| + args = parseArguments();
|
| + expr = delegate.createCallExpression(expr, args);
|
| + } else {
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return expr;
|
| + }
|
| +
|
| + // 11.3 Postfix Expressions
|
| +
|
| + var parsePostfixExpression = parseLeftHandSideExpression;
|
| +
|
| + // 11.4 Unary Operators
|
| +
|
| + function parseUnaryExpression() {
|
| + var token, expr;
|
| +
|
| + if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
|
| + expr = parsePostfixExpression();
|
| + } else if (match('+') || match('-') || match('!')) {
|
| + token = lex();
|
| + expr = parseUnaryExpression();
|
| + expr = delegate.createUnaryExpression(token.value, expr);
|
| + } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
|
| + throwError({}, Messages.UnexpectedToken);
|
| + } else {
|
| + expr = parsePostfixExpression();
|
| + }
|
| +
|
| + return expr;
|
| + }
|
| +
|
| + function binaryPrecedence(token) {
|
| + var prec = 0;
|
| +
|
| + if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
|
| + return 0;
|
| + }
|
| +
|
| + switch (token.value) {
|
| + case '||':
|
| + prec = 1;
|
| + break;
|
| +
|
| + case '&&':
|
| + prec = 2;
|
| + break;
|
| +
|
| + case '==':
|
| + case '!=':
|
| + case '===':
|
| + case '!==':
|
| + prec = 6;
|
| + break;
|
| +
|
| + case '<':
|
| + case '>':
|
| + case '<=':
|
| + case '>=':
|
| + case 'instanceof':
|
| + prec = 7;
|
| + break;
|
| +
|
| + case 'in':
|
| + prec = 7;
|
| + break;
|
| +
|
| + case '+':
|
| + case '-':
|
| + prec = 9;
|
| + break;
|
| +
|
| + case '*':
|
| + case '/':
|
| + case '%':
|
| + prec = 11;
|
| + break;
|
| +
|
| + default:
|
| + break;
|
| + }
|
| +
|
| + return prec;
|
| + }
|
| +
|
| + // 11.5 Multiplicative Operators
|
| + // 11.6 Additive Operators
|
| + // 11.7 Bitwise Shift Operators
|
| + // 11.8 Relational Operators
|
| + // 11.9 Equality Operators
|
| + // 11.10 Binary Bitwise Operators
|
| + // 11.11 Binary Logical Operators
|
| +
|
| + function parseBinaryExpression() {
|
| + var expr, token, prec, stack, right, operator, left, i;
|
| +
|
| + left = parseUnaryExpression();
|
| +
|
| + token = lookahead;
|
| + prec = binaryPrecedence(token);
|
| + if (prec === 0) {
|
| + return left;
|
| + }
|
| + token.prec = prec;
|
| + lex();
|
| +
|
| + right = parseUnaryExpression();
|
| +
|
| + stack = [left, token, right];
|
| +
|
| + while ((prec = binaryPrecedence(lookahead)) > 0) {
|
| +
|
| + // Reduce: make a binary expression from the three topmost entries.
|
| + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
|
| + right = stack.pop();
|
| + operator = stack.pop().value;
|
| + left = stack.pop();
|
| + expr = delegate.createBinaryExpression(operator, left, right);
|
| + stack.push(expr);
|
| + }
|
| +
|
| + // Shift.
|
| + token = lex();
|
| + token.prec = prec;
|
| + stack.push(token);
|
| + expr = parseUnaryExpression();
|
| + stack.push(expr);
|
| + }
|
| +
|
| + // Final reduce to clean-up the stack.
|
| + i = stack.length - 1;
|
| + expr = stack[i];
|
| + while (i > 1) {
|
| + expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
|
| + i -= 2;
|
| + }
|
| +
|
| + return expr;
|
| + }
|
| +
|
| +
|
| + // 11.12 Conditional Operator
|
| +
|
| + function parseConditionalExpression() {
|
| + var expr, consequent, alternate;
|
| +
|
| + expr = parseBinaryExpression();
|
| +
|
| + if (match('?')) {
|
| + lex();
|
| + consequent = parseConditionalExpression();
|
| + expect(':');
|
| + alternate = parseConditionalExpression();
|
| +
|
| + expr = delegate.createConditionalExpression(expr, consequent, alternate);
|
| + }
|
| +
|
| + return expr;
|
| + }
|
| +
|
| + // Simplification since we do not support AssignmentExpression.
|
| + var parseExpression = parseConditionalExpression;
|
| +
|
| + // Polymer Syntax extensions
|
| +
|
| + // Filter ::
|
| + // Identifier
|
| + // Identifier "(" ")"
|
| + // Identifier "(" FilterArguments ")"
|
| +
|
| + function parseFilter() {
|
| + var identifier, args;
|
| +
|
| + identifier = lex();
|
| +
|
| + if (identifier.type !== Token.Identifier) {
|
| + throwUnexpected(identifier);
|
| + }
|
| +
|
| + args = match('(') ? parseArguments() : [];
|
| +
|
| + return delegate.createFilter(identifier.value, args);
|
| + }
|
| +
|
| + // Filters ::
|
| + // "|" Filter
|
| + // Filters "|" Filter
|
| +
|
| + function parseFilters() {
|
| + while (match('|')) {
|
| + lex();
|
| + parseFilter();
|
| + }
|
| + }
|
| +
|
| + // TopLevel ::
|
| + // LabelledExpressions
|
| + // AsExpression
|
| + // InExpression
|
| + // FilterExpression
|
| +
|
| + // AsExpression ::
|
| + // FilterExpression as Identifier
|
| +
|
| + // InExpression ::
|
| + // Identifier, Identifier in FilterExpression
|
| + // Identifier in FilterExpression
|
| +
|
| + // FilterExpression ::
|
| + // Expression
|
| + // Expression Filters
|
| +
|
| + function parseTopLevel() {
|
| + skipWhitespace();
|
| + peek();
|
| +
|
| + var expr = parseExpression();
|
| + if (expr) {
|
| + if (lookahead.value === ',' || lookahead.value == 'in' &&
|
| + expr.type === Syntax.Identifier) {
|
| + parseInExpression(expr);
|
| + } else {
|
| + parseFilters();
|
| + if (lookahead.value === 'as') {
|
| + parseAsExpression(expr);
|
| + } else {
|
| + delegate.createTopLevel(expr);
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (lookahead.type !== Token.EOF) {
|
| + throwUnexpected(lookahead);
|
| + }
|
| + }
|
| +
|
| + function parseAsExpression(expr) {
|
| + lex(); // as
|
| + var identifier = lex().value;
|
| + delegate.createAsExpression(expr, identifier);
|
| + }
|
| +
|
| + function parseInExpression(identifier) {
|
| + var indexName;
|
| + if (lookahead.value === ',') {
|
| + lex();
|
| + if (lookahead.type !== Token.Identifier)
|
| + throwUnexpected(lookahead);
|
| + indexName = lex().value;
|
| + }
|
| +
|
| + lex(); // in
|
| + var expr = parseExpression();
|
| + parseFilters();
|
| + delegate.createInExpression(identifier.name, indexName, expr);
|
| + }
|
| +
|
| + function parse(code, inDelegate) {
|
| + delegate = inDelegate;
|
| + source = code;
|
| + index = 0;
|
| + length = source.length;
|
| + lookahead = null;
|
| + state = {
|
| + labelSet: {}
|
| + };
|
| +
|
| + return parseTopLevel();
|
| + }
|
| +
|
| + global.esprima = {
|
| + parse: parse
|
| + };
|
| +})(this);
|
| +
|
| +// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
| +// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +// Code distributed by Google as part of the polymer project is also
|
| +// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +
|
| +(function (global) {
|
| + 'use strict';
|
| +
|
| + function prepareBinding(expressionText, name, node, filterRegistry) {
|
| + var expression;
|
| + try {
|
| + expression = getExpression(expressionText);
|
| + if (expression.scopeIdent &&
|
| + (node.nodeType !== Node.ELEMENT_NODE ||
|
| + node.tagName !== 'TEMPLATE' ||
|
| + (name !== 'bind' && name !== 'repeat'))) {
|
| + throw Error('as and in can only be used within <template bind/repeat>');
|
| + }
|
| + } catch (ex) {
|
| + console.error('Invalid expression syntax: ' + expressionText, ex);
|
| + return;
|
| + }
|
| +
|
| + return function(model, node, oneTime) {
|
| + var binding = expression.getBinding(model, filterRegistry, oneTime);
|
| + if (expression.scopeIdent && binding) {
|
| + node.polymerExpressionScopeIdent_ = expression.scopeIdent;
|
| + if (expression.indexIdent)
|
| + node.polymerExpressionIndexIdent_ = expression.indexIdent;
|
| + }
|
| +
|
| + return binding;
|
| + }
|
| + }
|
| +
|
| + // TODO(rafaelw): Implement simple LRU.
|
| + var expressionParseCache = Object.create(null);
|
| +
|
| + function getExpression(expressionText) {
|
| + var expression = expressionParseCache[expressionText];
|
| + if (!expression) {
|
| + var delegate = new ASTDelegate();
|
| + esprima.parse(expressionText, delegate);
|
| + expression = new Expression(delegate);
|
| + expressionParseCache[expressionText] = expression;
|
| + }
|
| + return expression;
|
| + }
|
| +
|
| + function Literal(value) {
|
| + this.value = value;
|
| + this.valueFn_ = undefined;
|
| + }
|
| +
|
| + Literal.prototype = {
|
| + valueFn: function() {
|
| + if (!this.valueFn_) {
|
| + var value = this.value;
|
| + this.valueFn_ = function() {
|
| + return value;
|
| + }
|
| + }
|
| +
|
| + return this.valueFn_;
|
| + }
|
| + }
|
| +
|
| + function IdentPath(name) {
|
| + this.name = name;
|
| + this.path = Path.get(name);
|
| + }
|
| +
|
| + IdentPath.prototype = {
|
| + valueFn: function() {
|
| + if (!this.valueFn_) {
|
| + var name = this.name;
|
| + var path = this.path;
|
| + this.valueFn_ = function(model, observer) {
|
| + if (observer)
|
| + observer.addPath(model, path);
|
| +
|
| + return path.getValueFrom(model);
|
| + }
|
| + }
|
| +
|
| + return this.valueFn_;
|
| + },
|
| +
|
| + setValue: function(model, newValue) {
|
| + if (this.path.length == 1);
|
| + model = findScope(model, this.path[0]);
|
| +
|
| + return this.path.setValueFrom(model, newValue);
|
| + }
|
| + };
|
| +
|
| + function MemberExpression(object, property, accessor) {
|
| + this.computed = accessor == '[';
|
| +
|
| + this.dynamicDeps = typeof object == 'function' ||
|
| + object.dynamicDeps ||
|
| + (this.computed && !(property instanceof Literal));
|
| +
|
| + this.simplePath =
|
| + !this.dynamicDeps &&
|
| + (property instanceof IdentPath || property instanceof Literal) &&
|
| + (object instanceof MemberExpression || object instanceof IdentPath);
|
| +
|
| + this.object = this.simplePath ? object : getFn(object);
|
| + this.property = !this.computed || this.simplePath ?
|
| + property : getFn(property);
|
| + }
|
| +
|
| + MemberExpression.prototype = {
|
| + get fullPath() {
|
| + if (!this.fullPath_) {
|
| +
|
| + var parts = this.object instanceof MemberExpression ?
|
| + this.object.fullPath.slice() : [this.object.name];
|
| + parts.push(this.property instanceof IdentPath ?
|
| + this.property.name : this.property.value);
|
| + this.fullPath_ = Path.get(parts);
|
| + }
|
| +
|
| + return this.fullPath_;
|
| + },
|
| +
|
| + valueFn: function() {
|
| + if (!this.valueFn_) {
|
| + var object = this.object;
|
| +
|
| + if (this.simplePath) {
|
| + var path = this.fullPath;
|
| +
|
| + this.valueFn_ = function(model, observer) {
|
| + if (observer)
|
| + observer.addPath(model, path);
|
| +
|
| + return path.getValueFrom(model);
|
| + };
|
| + } else if (!this.computed) {
|
| + var path = Path.get(this.property.name);
|
| +
|
| + this.valueFn_ = function(model, observer, filterRegistry) {
|
| + var context = object(model, observer, filterRegistry);
|
| +
|
| + if (observer)
|
| + observer.addPath(context, path);
|
| +
|
| + return path.getValueFrom(context);
|
| + }
|
| + } else {
|
| + // Computed property.
|
| + var property = this.property;
|
| +
|
| + this.valueFn_ = function(model, observer, filterRegistry) {
|
| + var context = object(model, observer, filterRegistry);
|
| + var propName = property(model, observer, filterRegistry);
|
| + if (observer)
|
| + observer.addPath(context, [propName]);
|
| +
|
| + return context ? context[propName] : undefined;
|
| + };
|
| + }
|
| + }
|
| + return this.valueFn_;
|
| + },
|
| +
|
| + setValue: function(model, newValue) {
|
| + if (this.simplePath) {
|
| + this.fullPath.setValueFrom(model, newValue);
|
| + return newValue;
|
| + }
|
| +
|
| + var object = this.object(model);
|
| + var propName = this.property instanceof IdentPath ? this.property.name :
|
| + this.property(model);
|
| + return object[propName] = newValue;
|
| + }
|
| + };
|
| +
|
| + function Filter(name, args) {
|
| + this.name = name;
|
| + this.args = [];
|
| + for (var i = 0; i < args.length; i++) {
|
| + this.args[i] = getFn(args[i]);
|
| + }
|
| + }
|
| +
|
| + Filter.prototype = {
|
| + transform: function(model, observer, filterRegistry, toModelDirection,
|
| + initialArgs) {
|
| + var fn = filterRegistry[this.name];
|
| + var context = model;
|
| + if (fn) {
|
| + context = undefined;
|
| + } else {
|
| + fn = context[this.name];
|
| + if (!fn) {
|
| + console.error('Cannot find function or filter: ' + this.name);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + // If toModelDirection is falsey, then the "normal" (dom-bound) direction
|
| + // is used. Otherwise, it looks for a 'toModel' property function on the
|
| + // object.
|
| + if (toModelDirection) {
|
| + fn = fn.toModel;
|
| + } else if (typeof fn.toDOM == 'function') {
|
| + fn = fn.toDOM;
|
| + }
|
| +
|
| + if (typeof fn != 'function') {
|
| + console.error('Cannot find function or filter: ' + this.name);
|
| + return;
|
| + }
|
| +
|
| + var args = initialArgs || [];
|
| + for (var i = 0; i < this.args.length; i++) {
|
| + args.push(getFn(this.args[i])(model, observer, filterRegistry));
|
| + }
|
| +
|
| + return fn.apply(context, args);
|
| + }
|
| + };
|
| +
|
| + function notImplemented() { throw Error('Not Implemented'); }
|
| +
|
| + var unaryOperators = {
|
| + '+': function(v) { return +v; },
|
| + '-': function(v) { return -v; },
|
| + '!': function(v) { return !v; }
|
| + };
|
| +
|
| + var binaryOperators = {
|
| + '+': function(l, r) { return l+r; },
|
| + '-': function(l, r) { return l-r; },
|
| + '*': function(l, r) { return l*r; },
|
| + '/': function(l, r) { return l/r; },
|
| + '%': function(l, r) { return l%r; },
|
| + '<': function(l, r) { return l<r; },
|
| + '>': function(l, r) { return l>r; },
|
| + '<=': function(l, r) { return l<=r; },
|
| + '>=': function(l, r) { return l>=r; },
|
| + '==': function(l, r) { return l==r; },
|
| + '!=': function(l, r) { return l!=r; },
|
| + '===': function(l, r) { return l===r; },
|
| + '!==': function(l, r) { return l!==r; },
|
| + '&&': function(l, r) { return l&&r; },
|
| + '||': function(l, r) { return l||r; },
|
| + };
|
| +
|
| + function getFn(arg) {
|
| + return typeof arg == 'function' ? arg : arg.valueFn();
|
| + }
|
| +
|
| + function ASTDelegate() {
|
| + this.expression = null;
|
| + this.filters = [];
|
| + this.deps = {};
|
| + this.currentPath = undefined;
|
| + this.scopeIdent = undefined;
|
| + this.indexIdent = undefined;
|
| + this.dynamicDeps = false;
|
| + }
|
| +
|
| + ASTDelegate.prototype = {
|
| + createUnaryExpression: function(op, argument) {
|
| + if (!unaryOperators[op])
|
| + throw Error('Disallowed operator: ' + op);
|
| +
|
| + argument = getFn(argument);
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + return unaryOperators[op](argument(model, observer, filterRegistry));
|
| + };
|
| + },
|
| +
|
| + createBinaryExpression: function(op, left, right) {
|
| + if (!binaryOperators[op])
|
| + throw Error('Disallowed operator: ' + op);
|
| +
|
| + left = getFn(left);
|
| + right = getFn(right);
|
| +
|
| + switch (op) {
|
| + case '||':
|
| + this.dynamicDeps = true;
|
| + return function(model, observer, filterRegistry) {
|
| + return left(model, observer, filterRegistry) ||
|
| + right(model, observer, filterRegistry);
|
| + };
|
| + case '&&':
|
| + this.dynamicDeps = true;
|
| + return function(model, observer, filterRegistry) {
|
| + return left(model, observer, filterRegistry) &&
|
| + right(model, observer, filterRegistry);
|
| + };
|
| + }
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + return binaryOperators[op](left(model, observer, filterRegistry),
|
| + right(model, observer, filterRegistry));
|
| + };
|
| + },
|
| +
|
| + createConditionalExpression: function(test, consequent, alternate) {
|
| + test = getFn(test);
|
| + consequent = getFn(consequent);
|
| + alternate = getFn(alternate);
|
| +
|
| + this.dynamicDeps = true;
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + return test(model, observer, filterRegistry) ?
|
| + consequent(model, observer, filterRegistry) :
|
| + alternate(model, observer, filterRegistry);
|
| + }
|
| + },
|
| +
|
| + createIdentifier: function(name) {
|
| + var ident = new IdentPath(name);
|
| + ident.type = 'Identifier';
|
| + return ident;
|
| + },
|
| +
|
| + createMemberExpression: function(accessor, object, property) {
|
| + var ex = new MemberExpression(object, property, accessor);
|
| + if (ex.dynamicDeps)
|
| + this.dynamicDeps = true;
|
| + return ex;
|
| + },
|
| +
|
| + createCallExpression: function(expression, args) {
|
| + if (!(expression instanceof IdentPath))
|
| + throw Error('Only identifier function invocations are allowed');
|
| +
|
| + var filter = new Filter(expression.name, args);
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + return filter.transform(model, observer, filterRegistry, false);
|
| + };
|
| + },
|
| +
|
| + createLiteral: function(token) {
|
| + return new Literal(token.value);
|
| + },
|
| +
|
| + createArrayExpression: function(elements) {
|
| + for (var i = 0; i < elements.length; i++)
|
| + elements[i] = getFn(elements[i]);
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + var arr = []
|
| + for (var i = 0; i < elements.length; i++)
|
| + arr.push(elements[i](model, observer, filterRegistry));
|
| + return arr;
|
| + }
|
| + },
|
| +
|
| + createProperty: function(kind, key, value) {
|
| + return {
|
| + key: key instanceof IdentPath ? key.name : key.value,
|
| + value: value
|
| + };
|
| + },
|
| +
|
| + createObjectExpression: function(properties) {
|
| + for (var i = 0; i < properties.length; i++)
|
| + properties[i].value = getFn(properties[i].value);
|
| +
|
| + return function(model, observer, filterRegistry) {
|
| + var obj = {};
|
| + for (var i = 0; i < properties.length; i++)
|
| + obj[properties[i].key] =
|
| + properties[i].value(model, observer, filterRegistry);
|
| + return obj;
|
| + }
|
| + },
|
| +
|
| + createFilter: function(name, args) {
|
| + this.filters.push(new Filter(name, args));
|
| + },
|
| +
|
| + createAsExpression: function(expression, scopeIdent) {
|
| + this.expression = expression;
|
| + this.scopeIdent = scopeIdent;
|
| + },
|
| +
|
| + createInExpression: function(scopeIdent, indexIdent, expression) {
|
| + this.expression = expression;
|
| + this.scopeIdent = scopeIdent;
|
| + this.indexIdent = indexIdent;
|
| + },
|
| +
|
| + createTopLevel: function(expression) {
|
| + this.expression = expression;
|
| + },
|
| +
|
| + createThisExpression: notImplemented
|
| + }
|
| +
|
| + function ConstantObservable(value) {
|
| + this.value_ = value;
|
| + }
|
| +
|
| + ConstantObservable.prototype = {
|
| + open: function() { return this.value_; },
|
| + discardChanges: function() { return this.value_; },
|
| + deliver: function() {},
|
| + close: function() {},
|
| + }
|
| +
|
| + function Expression(delegate) {
|
| + this.scopeIdent = delegate.scopeIdent;
|
| + this.indexIdent = delegate.indexIdent;
|
| +
|
| + if (!delegate.expression)
|
| + throw Error('No expression found.');
|
| +
|
| + this.expression = delegate.expression;
|
| + getFn(this.expression); // forces enumeration of path dependencies
|
| +
|
| + this.filters = delegate.filters;
|
| + this.dynamicDeps = delegate.dynamicDeps;
|
| + }
|
| +
|
| + Expression.prototype = {
|
| + getBinding: function(model, filterRegistry, oneTime) {
|
| + if (oneTime)
|
| + return this.getValue(model, undefined, filterRegistry);
|
| +
|
| + var observer = new CompoundObserver();
|
| + // captures deps.
|
| + var firstValue = this.getValue(model, observer, filterRegistry);
|
| + var firstTime = true;
|
| + var self = this;
|
| +
|
| + function valueFn() {
|
| + // deps cannot have changed on first value retrieval.
|
| + if (firstTime) {
|
| + firstTime = false;
|
| + return firstValue;
|
| + }
|
| +
|
| + if (self.dynamicDeps)
|
| + observer.startReset();
|
| +
|
| + var value = self.getValue(model,
|
| + self.dynamicDeps ? observer : undefined,
|
| + filterRegistry);
|
| + if (self.dynamicDeps)
|
| + observer.finishReset();
|
| +
|
| + return value;
|
| + }
|
| +
|
| + function setValueFn(newValue) {
|
| + self.setValue(model, newValue, filterRegistry);
|
| + return newValue;
|
| + }
|
| +
|
| + return new ObserverTransform(observer, valueFn, setValueFn, true);
|
| + },
|
| +
|
| + getValue: function(model, observer, filterRegistry) {
|
| + var value = getFn(this.expression)(model, observer, filterRegistry);
|
| + for (var i = 0; i < this.filters.length; i++) {
|
| + value = this.filters[i].transform(model, observer, filterRegistry,
|
| + false, [value]);
|
| + }
|
| +
|
| + return value;
|
| + },
|
| +
|
| + setValue: function(model, newValue, filterRegistry) {
|
| + var count = this.filters ? this.filters.length : 0;
|
| + while (count-- > 0) {
|
| + newValue = this.filters[count].transform(model, undefined,
|
| + filterRegistry, true, [newValue]);
|
| + }
|
| +
|
| + if (this.expression.setValue)
|
| + return this.expression.setValue(model, newValue);
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Converts a style property name to a css property name. For example:
|
| + * "WebkitUserSelect" to "-webkit-user-select"
|
| + */
|
| + function convertStylePropertyName(name) {
|
| + return String(name).replace(/[A-Z]/g, function(c) {
|
| + return '-' + c.toLowerCase();
|
| + });
|
| + }
|
| +
|
| + var parentScopeName = '@' + Math.random().toString(36).slice(2);
|
| +
|
| + // Single ident paths must bind directly to the appropriate scope object.
|
| + // I.e. Pushed values in two-bindings need to be assigned to the actual model
|
| + // object.
|
| + function findScope(model, prop) {
|
| + while (model[parentScopeName] &&
|
| + !Object.prototype.hasOwnProperty.call(model, prop)) {
|
| + model = model[parentScopeName];
|
| + }
|
| +
|
| + return model;
|
| + }
|
| +
|
| + function isLiteralExpression(pathString) {
|
| + switch (pathString) {
|
| + case '':
|
| + return false;
|
| +
|
| + case 'false':
|
| + case 'null':
|
| + case 'true':
|
| + return true;
|
| + }
|
| +
|
| + if (!isNaN(Number(pathString)))
|
| + return true;
|
| +
|
| + return false;
|
| + };
|
| +
|
| + function PolymerExpressions() {}
|
| +
|
| + PolymerExpressions.prototype = {
|
| + // "built-in" filters
|
| + styleObject: function(value) {
|
| + var parts = [];
|
| + for (var key in value) {
|
| + parts.push(convertStylePropertyName(key) + ': ' + value[key]);
|
| + }
|
| + return parts.join('; ');
|
| + },
|
| +
|
| + tokenList: function(value) {
|
| + var tokens = [];
|
| + for (var key in value) {
|
| + if (value[key])
|
| + tokens.push(key);
|
| + }
|
| + return tokens.join(' ');
|
| + },
|
| +
|
| + // binding delegate API
|
| + prepareInstancePositionChanged: function(template) {
|
| + var indexIdent = template.polymerExpressionIndexIdent_;
|
| + if (!indexIdent)
|
| + return;
|
| +
|
| + return function(templateInstance, index) {
|
| + templateInstance.model[indexIdent] = index;
|
| + };
|
| + },
|
| +
|
| + prepareBinding: function(pathString, name, node) {
|
| + var path = Path.get(pathString);
|
| +
|
| + if (!isLiteralExpression(pathString) && path.valid) {
|
| + if (path.length == 1) {
|
| + return function(model, node, oneTime) {
|
| + if (oneTime)
|
| + return path.getValueFrom(model);
|
| +
|
| + var scope = findScope(model, path[0]);
|
| + return new PathObserver(scope, path);
|
| + };
|
| + }
|
| + return; // bail out early if pathString is simple path.
|
| + }
|
| +
|
| + return prepareBinding(pathString, name, node, this);
|
| + },
|
| +
|
| + prepareInstanceModel: function(template) {
|
| + var scopeName = template.polymerExpressionScopeIdent_;
|
| + if (!scopeName)
|
| + return;
|
| +
|
| + var parentScope = template.templateInstance ?
|
| + template.templateInstance.model :
|
| + template.model;
|
| +
|
| + var indexName = template.polymerExpressionIndexIdent_;
|
| +
|
| + return function(model) {
|
| + return createScopeObject(parentScope, model, scopeName, indexName);
|
| + };
|
| + }
|
| + };
|
| +
|
| + var createScopeObject = ('__proto__' in {}) ?
|
| + function(parentScope, model, scopeName, indexName) {
|
| + var scope = {};
|
| + scope[scopeName] = model;
|
| + scope[indexName] = undefined;
|
| + scope[parentScopeName] = parentScope;
|
| + scope.__proto__ = parentScope;
|
| + return scope;
|
| + } :
|
| + function(parentScope, model, scopeName, indexName) {
|
| + var scope = Object.create(parentScope);
|
| + Object.defineProperty(scope, scopeName,
|
| + { value: model, configurable: true, writable: true });
|
| + Object.defineProperty(scope, indexName,
|
| + { value: undefined, configurable: true, writable: true });
|
| + Object.defineProperty(scope, parentScopeName,
|
| + { value: parentScope, configurable: true, writable: true });
|
| + return scope;
|
| + };
|
| +
|
| + global.PolymerExpressions = PolymerExpressions;
|
| + PolymerExpressions.getExpression = getExpression;
|
| +})(this);
|
| +
|
| +Polymer = {
|
| + version: '0.5.1'
|
| +};
|
| +
|
| +// TODO(sorvell): this ensures Polymer is an object and not a function
|
| +// Platform is currently defining it as a function to allow for async loading
|
| +// of polymer; once we refine the loading process this likely goes away.
|
| +if (typeof window.Polymer === 'function') {
|
| + Polymer = {};
|
| +}
|
| +
|
| +
|
| +(function(scope) {
|
| +
|
| + function withDependencies(task, depends) {
|
| + depends = depends || [];
|
| + if (!depends.map) {
|
| + depends = [depends];
|
| + }
|
| + return task.apply(this, depends.map(marshal));
|
| + }
|
| +
|
| + function module(name, dependsOrFactory, moduleFactory) {
|
| + var module;
|
| + switch (arguments.length) {
|
| + case 0:
|
| + return;
|
| + case 1:
|
| + module = null;
|
| + break;
|
| + case 2:
|
| + // dependsOrFactory is `factory` in this case
|
| + module = dependsOrFactory.apply(this);
|
| + break;
|
| + default:
|
| + // dependsOrFactory is `depends` in this case
|
| + module = withDependencies(moduleFactory, dependsOrFactory);
|
| + break;
|
| + }
|
| + modules[name] = module;
|
| + };
|
| +
|
| + function marshal(name) {
|
| + return modules[name];
|
| + }
|
| +
|
| + var modules = {};
|
| +
|
| + function using(depends, task) {
|
| + HTMLImports.whenImportsReady(function() {
|
| + withDependencies(task, depends);
|
| + });
|
| + };
|
| +
|
| + // exports
|
| +
|
| + scope.marshal = marshal;
|
| + // `module` confuses commonjs detectors
|
| + scope.modularize = module;
|
| + scope.using = using;
|
| +
|
| +})(window);
|
| +
|
| +/*
|
| + Build only script.
|
| +
|
| + Ensures scripts needed for basic x-platform compatibility
|
| + will be run when platform.js is not loaded.
|
| + */
|
| +if (!window.WebComponents) {
|
| +
|
| +/*
|
| + On supported platforms, platform.js is not needed. To retain compatibility
|
| + with the polyfills, we stub out minimal functionality.
|
| + */
|
| +if (!window.WebComponents) {
|
| +
|
| + WebComponents = {
|
| + flush: function() {},
|
| + flags: {log: {}}
|
| + };
|
| +
|
| + Platform = WebComponents;
|
| +
|
| + CustomElements = {
|
| + useNative: true,
|
| + ready: true,
|
| + takeRecords: function() {},
|
| + instanceof: function(obj, base) {
|
| + return obj instanceof base;
|
| + }
|
| + };
|
| +
|
| + HTMLImports = {
|
| + useNative: true
|
| + };
|
| +
|
| +
|
| + addEventListener('HTMLImportsLoaded', function() {
|
| + document.dispatchEvent(
|
| + new CustomEvent('WebComponentsReady', {bubbles: true})
|
| + );
|
| + });
|
| +
|
| +
|
| + // ShadowDOM
|
| + ShadowDOMPolyfill = null;
|
| + wrap = unwrap = function(n){
|
| + return n;
|
| + };
|
| +
|
| +}
|
| +
|
| +/*
|
| + Create polyfill scope and feature detect native support.
|
| +*/
|
| +window.HTMLImports = window.HTMLImports || {flags:{}};
|
| +
|
| +(function(scope) {
|
| +
|
| +/**
|
| + Basic setup and simple module executer. We collect modules and then execute
|
| + the code later, only if it's necessary for polyfilling.
|
| +*/
|
| +var IMPORT_LINK_TYPE = 'import';
|
| +var useNative = Boolean(IMPORT_LINK_TYPE in document.createElement('link'));
|
| +
|
| +/**
|
| + Support `currentScript` on all browsers as `document._currentScript.`
|
| +
|
| + NOTE: We cannot polyfill `document.currentScript` because it's not possible
|
| + both to override and maintain the ability to capture the native value.
|
| + Therefore we choose to expose `_currentScript` both when native imports
|
| + and the polyfill are in use.
|
| +*/
|
| +// NOTE: ShadowDOMPolyfill intrusion.
|
| +var hasShadowDOMPolyfill = Boolean(window.ShadowDOMPolyfill);
|
| +var wrap = function(node) {
|
| + return hasShadowDOMPolyfill ? ShadowDOMPolyfill.wrapIfNeeded(node) : node;
|
| +};
|
| +var rootDocument = wrap(document);
|
| +
|
| +var currentScriptDescriptor = {
|
| + get: function() {
|
| + var script = HTMLImports.currentScript || document.currentScript ||
|
| + // NOTE: only works when called in synchronously executing code.
|
| + // readyState should check if `loading` but IE10 is
|
| + // interactive when scripts run so we cheat.
|
| + (document.readyState !== 'complete' ?
|
| + document.scripts[document.scripts.length - 1] : null);
|
| + return wrap(script);
|
| + },
|
| + configurable: true
|
| +};
|
| +
|
| +Object.defineProperty(document, '_currentScript', currentScriptDescriptor);
|
| +Object.defineProperty(rootDocument, '_currentScript', currentScriptDescriptor);
|
| +
|
| +/**
|
| + Add support for the `HTMLImportsLoaded` event and the `HTMLImports.whenReady`
|
| + method. This api is necessary because unlike the native implementation,
|
| + script elements do not force imports to resolve. Instead, users should wrap
|
| + code in either an `HTMLImportsLoaded` hander or after load time in an
|
| + `HTMLImports.whenReady(callback)` call.
|
| +
|
| + NOTE: This module also supports these apis under the native implementation.
|
| + Therefore, if this file is loaded, the same code can be used under both
|
| + the polyfill and native implementation.
|
| + */
|
| +
|
| +var isIE = /Trident/.test(navigator.userAgent);
|
| +
|
| +// call a callback when all HTMLImports in the document at call time
|
| +// (or at least document ready) have loaded.
|
| +// 1. ensure the document is in a ready state (has dom), then
|
| +// 2. watch for loading of imports and call callback when done
|
| +function whenReady(callback, doc) {
|
| + doc = doc || rootDocument;
|
| + // if document is loading, wait and try again
|
| + whenDocumentReady(function() {
|
| + watchImportsLoad(callback, doc);
|
| + }, doc);
|
| +}
|
| +
|
| +// call the callback when the document is in a ready state (has dom)
|
| +var requiredReadyState = isIE ? 'complete' : 'interactive';
|
| +var READY_EVENT = 'readystatechange';
|
| +function isDocumentReady(doc) {
|
| + return (doc.readyState === 'complete' ||
|
| + doc.readyState === requiredReadyState);
|
| +}
|
| +
|
| +// call <callback> when we ensure the document is in a ready state
|
| +function whenDocumentReady(callback, doc) {
|
| + if (!isDocumentReady(doc)) {
|
| + var checkReady = function() {
|
| + if (doc.readyState === 'complete' ||
|
| + doc.readyState === requiredReadyState) {
|
| + doc.removeEventListener(READY_EVENT, checkReady);
|
| + whenDocumentReady(callback, doc);
|
| + }
|
| + };
|
| + doc.addEventListener(READY_EVENT, checkReady);
|
| + } else if (callback) {
|
| + callback();
|
| + }
|
| +}
|
| +
|
| +function markTargetLoaded(event) {
|
| + event.target.__loaded = true;
|
| +}
|
| +
|
| +// call <callback> when we ensure all imports have loaded
|
| +function watchImportsLoad(callback, doc) {
|
| + var imports = doc.querySelectorAll('link[rel=import]');
|
| + var loaded = 0, l = imports.length;
|
| + function checkDone(d) {
|
| + if ((loaded == l) && callback) {
|
| + callback();
|
| + }
|
| + }
|
| + function loadedImport(e) {
|
| + markTargetLoaded(e);
|
| + loaded++;
|
| + checkDone();
|
| + }
|
| + if (l) {
|
| + for (var i=0, imp; (i<l) && (imp=imports[i]); i++) {
|
| + if (isImportLoaded(imp)) {
|
| + loadedImport.call(imp, {target: imp});
|
| + } else {
|
| + imp.addEventListener('load', loadedImport);
|
| + imp.addEventListener('error', loadedImport);
|
| + }
|
| + }
|
| + } else {
|
| + checkDone();
|
| + }
|
| +}
|
| +
|
| +// NOTE: test for native imports loading is based on explicitly watching
|
| +// all imports (see below).
|
| +// However, we cannot rely on this entirely without watching the entire document
|
| +// for import links. For perf reasons, currently only head is watched.
|
| +// Instead, we fallback to checking if the import property is available
|
| +// and the document is not itself loading.
|
| +function isImportLoaded(link) {
|
| + return useNative ? link.__loaded ||
|
| + (link.import && link.import.readyState !== 'loading') :
|
| + link.__importParsed;
|
| +}
|
| +
|
| +// TODO(sorvell): Workaround for
|
| +// https://www.w3.org/Bugs/Public/show_bug.cgi?id=25007, should be removed when
|
| +// this bug is addressed.
|
| +// (1) Install a mutation observer to see when HTMLImports have loaded
|
| +// (2) if this script is run during document load it will watch any existing
|
| +// imports for loading.
|
| +//
|
| +// NOTE: The workaround has restricted functionality: (1) it's only compatible
|
| +// with imports that are added to document.head since the mutation observer
|
| +// watches only head for perf reasons, (2) it requires this script
|
| +// to run before any imports have completed loading.
|
| +if (useNative) {
|
| + new MutationObserver(function(mxns) {
|
| + for (var i=0, l=mxns.length, m; (i < l) && (m=mxns[i]); i++) {
|
| + if (m.addedNodes) {
|
| + handleImports(m.addedNodes);
|
| + }
|
| + }
|
| + }).observe(document.head, {childList: true});
|
| +
|
| + function handleImports(nodes) {
|
| + for (var i=0, l=nodes.length, n; (i<l) && (n=nodes[i]); i++) {
|
| + if (isImport(n)) {
|
| + handleImport(n);
|
| + }
|
| + }
|
| + }
|
| +
|
| + function isImport(element) {
|
| + return element.localName === 'link' && element.rel === 'import';
|
| + }
|
| +
|
| + function handleImport(element) {
|
| + var loaded = element.import;
|
| + if (loaded) {
|
| + markTargetLoaded({target: element});
|
| + } else {
|
| + element.addEventListener('load', markTargetLoaded);
|
| + element.addEventListener('error', markTargetLoaded);
|
| + }
|
| + }
|
| +
|
| + // make sure to catch any imports that are in the process of loading
|
| + // when this script is run.
|
| + (function() {
|
| + if (document.readyState === 'loading') {
|
| + var imports = document.querySelectorAll('link[rel=import]');
|
| + for (var i=0, l=imports.length, imp; (i<l) && (imp=imports[i]); i++) {
|
| + handleImport(imp);
|
| + }
|
| + }
|
| + })();
|
| +
|
| +}
|
| +
|
| +// Fire the 'HTMLImportsLoaded' event when imports in document at load time
|
| +// have loaded. This event is required to simulate the script blocking
|
| +// behavior of native imports. A main document script that needs to be sure
|
| +// imports have loaded should wait for this event.
|
| +whenReady(function() {
|
| + HTMLImports.ready = true;
|
| + HTMLImports.readyTime = new Date().getTime();
|
| + rootDocument.dispatchEvent(
|
| + new CustomEvent('HTMLImportsLoaded', {bubbles: true})
|
| + );
|
| +});
|
| +
|
| +// exports
|
| +scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
|
| +scope.useNative = useNative;
|
| +scope.rootDocument = rootDocument;
|
| +scope.whenReady = whenReady;
|
| +scope.isIE = isIE;
|
| +
|
| +})(HTMLImports);
|
| +
|
| +(function(scope) {
|
| +
|
| + // TODO(sorvell): It's desireable to provide a default stylesheet
|
| + // that's convenient for styling unresolved elements, but
|
| + // it's cumbersome to have to include this manually in every page.
|
| + // It would make sense to put inside some HTMLImport but
|
| + // the HTMLImports polyfill does not allow loading of stylesheets
|
| + // that block rendering. Therefore this injection is tolerated here.
|
| + var style = document.createElement('style');
|
| + style.textContent = ''
|
| + + 'body {'
|
| + + 'transition: opacity ease-in 0.2s;'
|
| + + ' } \n'
|
| + + 'body[unresolved] {'
|
| + + 'opacity: 0; display: block; overflow: hidden;'
|
| + + ' } \n'
|
| + ;
|
| + var head = document.querySelector('head');
|
| + head.insertBefore(style, head.firstChild);
|
| +
|
| +})(Platform);
|
| +
|
| +/*
|
| + Build only script.
|
| +
|
| + Ensures scripts needed for basic x-platform compatibility
|
| + will be run when platform.js is not loaded.
|
| + */
|
| +}
|
| +(function(global) {
|
| + 'use strict';
|
| +
|
| + var testingExposeCycleCount = global.testingExposeCycleCount;
|
| +
|
| + // Detect and do basic sanity checking on Object/Array.observe.
|
| + function detectObjectObserve() {
|
| + if (typeof Object.observe !== 'function' ||
|
| + typeof Array.observe !== 'function') {
|
| + return false;
|
| + }
|
| +
|
| + var records = [];
|
| +
|
| + function callback(recs) {
|
| + records = recs;
|
| + }
|
| +
|
| + var test = {};
|
| + var arr = [];
|
| + Object.observe(test, callback);
|
| + Array.observe(arr, callback);
|
| + test.id = 1;
|
| + test.id = 2;
|
| + delete test.id;
|
| + arr.push(1, 2);
|
| + arr.length = 0;
|
| +
|
| + Object.deliverChangeRecords(callback);
|
| + if (records.length !== 5)
|
| + return false;
|
| +
|
| + if (records[0].type != 'add' ||
|
| + records[1].type != 'update' ||
|
| + records[2].type != 'delete' ||
|
| + records[3].type != 'splice' ||
|
| + records[4].type != 'splice') {
|
| + return false;
|
| + }
|
| +
|
| + Object.unobserve(test, callback);
|
| + Array.unobserve(arr, callback);
|
| +
|
| + return true;
|
| + }
|
| +
|
| + var hasObserve = detectObjectObserve();
|
| +
|
| + function detectEval() {
|
| + // Don't test for eval if we're running in a Chrome App environment.
|
| + // We check for APIs set that only exist in a Chrome App context.
|
| + if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
|
| + return false;
|
| + }
|
| +
|
| + // Firefox OS Apps do not allow eval. This feature detection is very hacky
|
| + // but even if some other platform adds support for this function this code
|
| + // will continue to work.
|
| + if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
|
| + return false;
|
| + }
|
| +
|
| + try {
|
| + var f = new Function('', 'return true;');
|
| + return f();
|
| + } catch (ex) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + var hasEval = detectEval();
|
| +
|
| + function isIndex(s) {
|
| + return +s === s >>> 0 && s !== '';
|
| + }
|
| +
|
| + function toNumber(s) {
|
| + return +s;
|
| + }
|
| +
|
| + function isObject(obj) {
|
| + return obj === Object(obj);
|
| + }
|
| +
|
| + var numberIsNaN = global.Number.isNaN || function(value) {
|
| + return typeof value === 'number' && global.isNaN(value);
|
| + }
|
| +
|
| + function areSameValue(left, right) {
|
| + if (left === right)
|
| + return left !== 0 || 1 / left === 1 / right;
|
| + if (numberIsNaN(left) && numberIsNaN(right))
|
| + return true;
|
| +
|
| + return left !== left && right !== right;
|
| + }
|
| +
|
| + var createObject = ('__proto__' in {}) ?
|
| + function(obj) { return obj; } :
|
| + function(obj) {
|
| + var proto = obj.__proto__;
|
| + if (!proto)
|
| + return obj;
|
| + var newObject = Object.create(proto);
|
| + Object.getOwnPropertyNames(obj).forEach(function(name) {
|
| + Object.defineProperty(newObject, name,
|
| + Object.getOwnPropertyDescriptor(obj, name));
|
| + });
|
| + return newObject;
|
| + };
|
| +
|
| + var identStart = '[\$_a-zA-Z]';
|
| + var identPart = '[\$_a-zA-Z0-9]';
|
| + var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
|
| +
|
| + function getPathCharType(char) {
|
| + if (char === undefined)
|
| + return 'eof';
|
| +
|
| + var code = char.charCodeAt(0);
|
| +
|
| + switch(code) {
|
| + case 0x5B: // [
|
| + case 0x5D: // ]
|
| + case 0x2E: // .
|
| + case 0x22: // "
|
| + case 0x27: // '
|
| + case 0x30: // 0
|
| + return char;
|
| +
|
| + case 0x5F: // _
|
| + case 0x24: // $
|
| + return 'ident';
|
| +
|
| + case 0x20: // Space
|
| + case 0x09: // Tab
|
| + case 0x0A: // Newline
|
| + case 0x0D: // Return
|
| + case 0xA0: // No-break space
|
| + case 0xFEFF: // Byte Order Mark
|
| + case 0x2028: // Line Separator
|
| + case 0x2029: // Paragraph Separator
|
| + return 'ws';
|
| + }
|
| +
|
| + // a-z, A-Z
|
| + if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
|
| + return 'ident';
|
| +
|
| + // 1-9
|
| + if (0x31 <= code && code <= 0x39)
|
| + return 'number';
|
| +
|
| + return 'else';
|
| + }
|
| +
|
| + var pathStateMachine = {
|
| + 'beforePath': {
|
| + 'ws': ['beforePath'],
|
| + 'ident': ['inIdent', 'append'],
|
| + '[': ['beforeElement'],
|
| + 'eof': ['afterPath']
|
| + },
|
| +
|
| + 'inPath': {
|
| + 'ws': ['inPath'],
|
| + '.': ['beforeIdent'],
|
| + '[': ['beforeElement'],
|
| + 'eof': ['afterPath']
|
| + },
|
| +
|
| + 'beforeIdent': {
|
| + 'ws': ['beforeIdent'],
|
| + 'ident': ['inIdent', 'append']
|
| + },
|
| +
|
| + 'inIdent': {
|
| + 'ident': ['inIdent', 'append'],
|
| + '0': ['inIdent', 'append'],
|
| + 'number': ['inIdent', 'append'],
|
| + 'ws': ['inPath', 'push'],
|
| + '.': ['beforeIdent', 'push'],
|
| + '[': ['beforeElement', 'push'],
|
| + 'eof': ['afterPath', 'push']
|
| + },
|
| +
|
| + 'beforeElement': {
|
| + 'ws': ['beforeElement'],
|
| + '0': ['afterZero', 'append'],
|
| + 'number': ['inIndex', 'append'],
|
| + "'": ['inSingleQuote', 'append', ''],
|
| + '"': ['inDoubleQuote', 'append', '']
|
| + },
|
| +
|
| + 'afterZero': {
|
| + 'ws': ['afterElement', 'push'],
|
| + ']': ['inPath', 'push']
|
| + },
|
| +
|
| + 'inIndex': {
|
| + '0': ['inIndex', 'append'],
|
| + 'number': ['inIndex', 'append'],
|
| + 'ws': ['afterElement'],
|
| + ']': ['inPath', 'push']
|
| + },
|
| +
|
| + 'inSingleQuote': {
|
| + "'": ['afterElement'],
|
| + 'eof': ['error'],
|
| + 'else': ['inSingleQuote', 'append']
|
| + },
|
| +
|
| + 'inDoubleQuote': {
|
| + '"': ['afterElement'],
|
| + 'eof': ['error'],
|
| + 'else': ['inDoubleQuote', 'append']
|
| + },
|
| +
|
| + 'afterElement': {
|
| + 'ws': ['afterElement'],
|
| + ']': ['inPath', 'push']
|
| + }
|
| + }
|
| +
|
| + function noop() {}
|
| +
|
| + function parsePath(path) {
|
| + var keys = [];
|
| + var index = -1;
|
| + var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
|
| +
|
| + var actions = {
|
| + push: function() {
|
| + if (key === undefined)
|
| + return;
|
| +
|
| + keys.push(key);
|
| + key = undefined;
|
| + },
|
| +
|
| + append: function() {
|
| + if (key === undefined)
|
| + key = newChar
|
| + else
|
| + key += newChar;
|
| + }
|
| + };
|
| +
|
| + function maybeUnescapeQuote() {
|
| + if (index >= path.length)
|
| + return;
|
| +
|
| + var nextChar = path[index + 1];
|
| + if ((mode == 'inSingleQuote' && nextChar == "'") ||
|
| + (mode == 'inDoubleQuote' && nextChar == '"')) {
|
| + index++;
|
| + newChar = nextChar;
|
| + actions.append();
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + while (mode) {
|
| + index++;
|
| + c = path[index];
|
| +
|
| + if (c == '\\' && maybeUnescapeQuote(mode))
|
| + continue;
|
| +
|
| + type = getPathCharType(c);
|
| + typeMap = pathStateMachine[mode];
|
| + transition = typeMap[type] || typeMap['else'] || 'error';
|
| +
|
| + if (transition == 'error')
|
| + return; // parse error;
|
| +
|
| + mode = transition[0];
|
| + action = actions[transition[1]] || noop;
|
| + newChar = transition[2] === undefined ? c : transition[2];
|
| + action();
|
| +
|
| + if (mode === 'afterPath') {
|
| + return keys;
|
| + }
|
| + }
|
| +
|
| + return; // parse error
|
| + }
|
| +
|
| + function isIdent(s) {
|
| + return identRegExp.test(s);
|
| + }
|
| +
|
| + var constructorIsPrivate = {};
|
| +
|
| + function Path(parts, privateToken) {
|
| + if (privateToken !== constructorIsPrivate)
|
| + throw Error('Use Path.get to retrieve path objects');
|
| +
|
| + for (var i = 0; i < parts.length; i++) {
|
| + this.push(String(parts[i]));
|
| + }
|
| +
|
| + if (hasEval && this.length) {
|
| + this.getValueFrom = this.compiledGetValueFromFn();
|
| + }
|
| + }
|
| +
|
| + // TODO(rafaelw): Make simple LRU cache
|
| + var pathCache = {};
|
| +
|
| + function getPath(pathString) {
|
| + if (pathString instanceof Path)
|
| + return pathString;
|
| +
|
| + if (pathString == null || pathString.length == 0)
|
| + pathString = '';
|
| +
|
| + if (typeof pathString != 'string') {
|
| + if (isIndex(pathString.length)) {
|
| + // Constructed with array-like (pre-parsed) keys
|
| + return new Path(pathString, constructorIsPrivate);
|
| + }
|
| +
|
| + pathString = String(pathString);
|
| + }
|
| +
|
| + var path = pathCache[pathString];
|
| + if (path)
|
| + return path;
|
| +
|
| + var parts = parsePath(pathString);
|
| + if (!parts)
|
| + return invalidPath;
|
| +
|
| + var path = new Path(parts, constructorIsPrivate);
|
| + pathCache[pathString] = path;
|
| + return path;
|
| + }
|
| +
|
| + Path.get = getPath;
|
| +
|
| + function formatAccessor(key) {
|
| + if (isIndex(key)) {
|
| + return '[' + key + ']';
|
| + } else {
|
| + return '["' + key.replace(/"/g, '\\"') + '"]';
|
| + }
|
| + }
|
| +
|
| + Path.prototype = createObject({
|
| + __proto__: [],
|
| + valid: true,
|
| +
|
| + toString: function() {
|
| + var pathString = '';
|
| + for (var i = 0; i < this.length; i++) {
|
| + var key = this[i];
|
| + if (isIdent(key)) {
|
| + pathString += i ? '.' + key : key;
|
| + } else {
|
| + pathString += formatAccessor(key);
|
| + }
|
| + }
|
| +
|
| + return pathString;
|
| + },
|
| +
|
| + getValueFrom: function(obj, directObserver) {
|
| + for (var i = 0; i < this.length; i++) {
|
| + if (obj == null)
|
| + return;
|
| + obj = obj[this[i]];
|
| + }
|
| + return obj;
|
| + },
|
| +
|
| + iterateObjects: function(obj, observe) {
|
| + for (var i = 0; i < this.length; i++) {
|
| + if (i)
|
| + obj = obj[this[i - 1]];
|
| + if (!isObject(obj))
|
| + return;
|
| + observe(obj, this[i]);
|
| + }
|
| + },
|
| +
|
| + compiledGetValueFromFn: function() {
|
| + var str = '';
|
| + var pathString = 'obj';
|
| + str += 'if (obj != null';
|
| + var i = 0;
|
| + var key;
|
| + for (; i < (this.length - 1); i++) {
|
| + key = this[i];
|
| + pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
| + str += ' &&\n ' + pathString + ' != null';
|
| + }
|
| + str += ')\n';
|
| +
|
| + var key = this[i];
|
| + pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
| +
|
| + str += ' return ' + pathString + ';\nelse\n return undefined;';
|
| + return new Function('obj', str);
|
| + },
|
| +
|
| + setValueFrom: function(obj, value) {
|
| + if (!this.length)
|
| + return false;
|
| +
|
| + for (var i = 0; i < this.length - 1; i++) {
|
| + if (!isObject(obj))
|
| + return false;
|
| + obj = obj[this[i]];
|
| + }
|
| +
|
| + if (!isObject(obj))
|
| + return false;
|
| +
|
| + obj[this[i]] = value;
|
| + return true;
|
| + }
|
| + });
|
| +
|
| + var invalidPath = new Path('', constructorIsPrivate);
|
| + invalidPath.valid = false;
|
| + invalidPath.getValueFrom = invalidPath.setValueFrom = function() {};
|
| +
|
| + var MAX_DIRTY_CHECK_CYCLES = 1000;
|
| +
|
| + function dirtyCheck(observer) {
|
| + var cycles = 0;
|
| + while (cycles < MAX_DIRTY_CHECK_CYCLES && observer.check_()) {
|
| + cycles++;
|
| + }
|
| + if (testingExposeCycleCount)
|
| + global.dirtyCheckCycleCount = cycles;
|
| +
|
| + return cycles > 0;
|
| + }
|
| +
|
| + function objectIsEmpty(object) {
|
| + for (var prop in object)
|
| + return false;
|
| + return true;
|
| + }
|
| +
|
| + function diffIsEmpty(diff) {
|
| + return objectIsEmpty(diff.added) &&
|
| + objectIsEmpty(diff.removed) &&
|
| + objectIsEmpty(diff.changed);
|
| + }
|
| +
|
| + function diffObjectFromOldObject(object, oldObject) {
|
| + var added = {};
|
| + var removed = {};
|
| + var changed = {};
|
| +
|
| + for (var prop in oldObject) {
|
| + var newValue = object[prop];
|
| +
|
| + if (newValue !== undefined && newValue === oldObject[prop])
|
| + continue;
|
| +
|
| + if (!(prop in object)) {
|
| + removed[prop] = undefined;
|
| + continue;
|
| + }
|
| +
|
| + if (newValue !== oldObject[prop])
|
| + changed[prop] = newValue;
|
| + }
|
| +
|
| + for (var prop in object) {
|
| + if (prop in oldObject)
|
| + continue;
|
| +
|
| + added[prop] = object[prop];
|
| + }
|
| +
|
| + if (Array.isArray(object) && object.length !== oldObject.length)
|
| + changed.length = object.length;
|
| +
|
| + return {
|
| + added: added,
|
| + removed: removed,
|
| + changed: changed
|
| + };
|
| + }
|
| +
|
| + var eomTasks = [];
|
| + function runEOMTasks() {
|
| + if (!eomTasks.length)
|
| + return false;
|
| +
|
| + for (var i = 0; i < eomTasks.length; i++) {
|
| + eomTasks[i]();
|
| + }
|
| + eomTasks.length = 0;
|
| + return true;
|
| + }
|
| +
|
| + var runEOM = hasObserve ? (function(){
|
| + return function(fn) {
|
| + return Promise.resolve().then(fn);
|
| + }
|
| + })() :
|
| + (function() {
|
| + return function(fn) {
|
| + eomTasks.push(fn);
|
| + };
|
| + })();
|
| +
|
| + var observedObjectCache = [];
|
| +
|
| + function newObservedObject() {
|
| + var observer;
|
| + var object;
|
| + var discardRecords = false;
|
| + var first = true;
|
| +
|
| + function callback(records) {
|
| + if (observer && observer.state_ === OPENED && !discardRecords)
|
| + observer.check_(records);
|
| + }
|
| +
|
| + return {
|
| + open: function(obs) {
|
| + if (observer)
|
| + throw Error('ObservedObject in use');
|
| +
|
| + if (!first)
|
| + Object.deliverChangeRecords(callback);
|
| +
|
| + observer = obs;
|
| + first = false;
|
| + },
|
| + observe: function(obj, arrayObserve) {
|
| + object = obj;
|
| + if (arrayObserve)
|
| + Array.observe(object, callback);
|
| + else
|
| + Object.observe(object, callback);
|
| + },
|
| + deliver: function(discard) {
|
| + discardRecords = discard;
|
| + Object.deliverChangeRecords(callback);
|
| + discardRecords = false;
|
| + },
|
| + close: function() {
|
| + observer = undefined;
|
| + Object.unobserve(object, callback);
|
| + observedObjectCache.push(this);
|
| + }
|
| + };
|
| + }
|
| +
|
| + /*
|
| + * The observedSet abstraction is a perf optimization which reduces the total
|
| + * number of Object.observe observations of a set of objects. The idea is that
|
| + * groups of Observers will have some object dependencies in common and this
|
| + * observed set ensures that each object in the transitive closure of
|
| + * dependencies is only observed once. The observedSet acts as a write barrier
|
| + * such that whenever any change comes through, all Observers are checked for
|
| + * changed values.
|
| + *
|
| + * Note that this optimization is explicitly moving work from setup-time to
|
| + * change-time.
|
| + *
|
| + * TODO(rafaelw): Implement "garbage collection". In order to move work off
|
| + * the critical path, when Observers are closed, their observed objects are
|
| + * not Object.unobserve(d). As a result, it's possible that if the observedSet
|
| + * is kept open, but some Observers have been closed, it could cause "leaks"
|
| + * (prevent otherwise collectable objects from being collected). At some
|
| + * point, we should implement incremental "gc" which keeps a list of
|
| + * observedSets which may need clean-up and does small amounts of cleanup on a
|
| + * timeout until all is clean.
|
| + */
|
| +
|
| + function getObservedObject(observer, object, arrayObserve) {
|
| + var dir = observedObjectCache.pop() || newObservedObject();
|
| + dir.open(observer);
|
| + dir.observe(object, arrayObserve);
|
| + return dir;
|
| + }
|
| +
|
| + var observedSetCache = [];
|
| +
|
| + function newObservedSet() {
|
| + var observerCount = 0;
|
| + var observers = [];
|
| + var objects = [];
|
| + var rootObj;
|
| + var rootObjProps;
|
| +
|
| + function observe(obj, prop) {
|
| + if (!obj)
|
| + return;
|
| +
|
| + if (obj === rootObj)
|
| + rootObjProps[prop] = true;
|
| +
|
| + if (objects.indexOf(obj) < 0) {
|
| + objects.push(obj);
|
| + Object.observe(obj, callback);
|
| + }
|
| +
|
| + observe(Object.getPrototypeOf(obj), prop);
|
| + }
|
| +
|
| + function allRootObjNonObservedProps(recs) {
|
| + for (var i = 0; i < recs.length; i++) {
|
| + var rec = recs[i];
|
| + if (rec.object !== rootObj ||
|
| + rootObjProps[rec.name] ||
|
| + rec.type === 'setPrototype') {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + function callback(recs) {
|
| + if (allRootObjNonObservedProps(recs))
|
| + return;
|
| +
|
| + var observer;
|
| + for (var i = 0; i < observers.length; i++) {
|
| + observer = observers[i];
|
| + if (observer.state_ == OPENED) {
|
| + observer.iterateObjects_(observe);
|
| + }
|
| + }
|
| +
|
| + for (var i = 0; i < observers.length; i++) {
|
| + observer = observers[i];
|
| + if (observer.state_ == OPENED) {
|
| + observer.check_();
|
| + }
|
| + }
|
| + }
|
| +
|
| + var record = {
|
| + objects: objects,
|
| + get rootObject() { return rootObj; },
|
| + set rootObject(value) {
|
| + rootObj = value;
|
| + rootObjProps = {};
|
| + },
|
| + open: function(obs, object) {
|
| + observers.push(obs);
|
| + observerCount++;
|
| + obs.iterateObjects_(observe);
|
| + },
|
| + close: function(obs) {
|
| + observerCount--;
|
| + if (observerCount > 0) {
|
| + return;
|
| + }
|
| +
|
| + for (var i = 0; i < objects.length; i++) {
|
| + Object.unobserve(objects[i], callback);
|
| + Observer.unobservedCount++;
|
| + }
|
| +
|
| + observers.length = 0;
|
| + objects.length = 0;
|
| + rootObj = undefined;
|
| + rootObjProps = undefined;
|
| + observedSetCache.push(this);
|
| + if (lastObservedSet === this)
|
| + lastObservedSet = null;
|
| + },
|
| + };
|
| +
|
| + return record;
|
| + }
|
| +
|
| + var lastObservedSet;
|
| +
|
| + function getObservedSet(observer, obj) {
|
| + if (!lastObservedSet || lastObservedSet.rootObject !== obj) {
|
| + lastObservedSet = observedSetCache.pop() || newObservedSet();
|
| + lastObservedSet.rootObject = obj;
|
| + }
|
| + lastObservedSet.open(observer, obj);
|
| + return lastObservedSet;
|
| + }
|
| +
|
| + var UNOPENED = 0;
|
| + var OPENED = 1;
|
| + var CLOSED = 2;
|
| + var RESETTING = 3;
|
| +
|
| + var nextObserverId = 1;
|
| +
|
| + function Observer() {
|
| + this.state_ = UNOPENED;
|
| + this.callback_ = undefined;
|
| + this.target_ = undefined; // TODO(rafaelw): Should be WeakRef
|
| + this.directObserver_ = undefined;
|
| + this.value_ = undefined;
|
| + this.id_ = nextObserverId++;
|
| + }
|
| +
|
| + Observer.prototype = {
|
| + open: function(callback, target) {
|
| + if (this.state_ != UNOPENED)
|
| + throw Error('Observer has already been opened.');
|
| +
|
| + addToAll(this);
|
| + this.callback_ = callback;
|
| + this.target_ = target;
|
| + this.connect_();
|
| + this.state_ = OPENED;
|
| + return this.value_;
|
| + },
|
| +
|
| + close: function() {
|
| + if (this.state_ != OPENED)
|
| + return;
|
| +
|
| + removeFromAll(this);
|
| + this.disconnect_();
|
| + this.value_ = undefined;
|
| + this.callback_ = undefined;
|
| + this.target_ = undefined;
|
| + this.state_ = CLOSED;
|
| + },
|
| +
|
| + deliver: function() {
|
| + if (this.state_ != OPENED)
|
| + return;
|
| +
|
| + dirtyCheck(this);
|
| + },
|
| +
|
| + report_: function(changes) {
|
| + try {
|
| + this.callback_.apply(this.target_, changes);
|
| + } catch (ex) {
|
| + Observer._errorThrownDuringCallback = true;
|
| + console.error('Exception caught during observer callback: ' +
|
| + (ex.stack || ex));
|
| + }
|
| + },
|
| +
|
| + discardChanges: function() {
|
| + this.check_(undefined, true);
|
| + return this.value_;
|
| + }
|
| + }
|
| +
|
| + var collectObservers = !hasObserve;
|
| + var allObservers;
|
| + Observer._allObserversCount = 0;
|
| +
|
| + if (collectObservers) {
|
| + allObservers = [];
|
| + }
|
| +
|
| + function addToAll(observer) {
|
| + Observer._allObserversCount++;
|
| + if (!collectObservers)
|
| + return;
|
| +
|
| + allObservers.push(observer);
|
| + }
|
| +
|
| + function removeFromAll(observer) {
|
| + Observer._allObserversCount--;
|
| + }
|
| +
|
| + var runningMicrotaskCheckpoint = false;
|
| +
|
| + global.Platform = global.Platform || {};
|
| +
|
| + global.Platform.performMicrotaskCheckpoint = function() {
|
| + if (runningMicrotaskCheckpoint)
|
| + return;
|
| +
|
| + if (!collectObservers)
|
| + return;
|
| +
|
| + runningMicrotaskCheckpoint = true;
|
| +
|
| + var cycles = 0;
|
| + var anyChanged, toCheck;
|
| +
|
| + do {
|
| + cycles++;
|
| + toCheck = allObservers;
|
| + allObservers = [];
|
| + anyChanged = false;
|
| +
|
| + for (var i = 0; i < toCheck.length; i++) {
|
| + var observer = toCheck[i];
|
| + if (observer.state_ != OPENED)
|
| + continue;
|
| +
|
| + if (observer.check_())
|
| + anyChanged = true;
|
| +
|
| + allObservers.push(observer);
|
| + }
|
| + if (runEOMTasks())
|
| + anyChanged = true;
|
| + } while (cycles < MAX_DIRTY_CHECK_CYCLES && anyChanged);
|
| +
|
| + if (testingExposeCycleCount)
|
| + global.dirtyCheckCycleCount = cycles;
|
| +
|
| + runningMicrotaskCheckpoint = false;
|
| + };
|
| +
|
| + if (collectObservers) {
|
| + global.Platform.clearObservers = function() {
|
| + allObservers = [];
|
| + };
|
| + }
|
| +
|
| + function ObjectObserver(object) {
|
| + Observer.call(this);
|
| + this.value_ = object;
|
| + this.oldObject_ = undefined;
|
| + }
|
| +
|
| + ObjectObserver.prototype = createObject({
|
| + __proto__: Observer.prototype,
|
| +
|
| + arrayObserve: false,
|
| +
|
| + connect_: function(callback, target) {
|
| + if (hasObserve) {
|
| + this.directObserver_ = getObservedObject(this, this.value_,
|
| + this.arrayObserve);
|
| + } else {
|
| + this.oldObject_ = this.copyObject(this.value_);
|
| + }
|
| +
|
| + },
|
| +
|
| + copyObject: function(object) {
|
| + var copy = Array.isArray(object) ? [] : {};
|
| + for (var prop in object) {
|
| + copy[prop] = object[prop];
|
| + };
|
| + if (Array.isArray(object))
|
| + copy.length = object.length;
|
| + return copy;
|
| + },
|
| +
|
| + check_: function(changeRecords, skipChanges) {
|
| + var diff;
|
| + var oldValues;
|
| + if (hasObserve) {
|
| + if (!changeRecords)
|
| + return false;
|
| +
|
| + oldValues = {};
|
| + diff = diffObjectFromChangeRecords(this.value_, changeRecords,
|
| + oldValues);
|
| + } else {
|
| + oldValues = this.oldObject_;
|
| + diff = diffObjectFromOldObject(this.value_, this.oldObject_);
|
| + }
|
| +
|
| + if (diffIsEmpty(diff))
|
| + return false;
|
| +
|
| + if (!hasObserve)
|
| + this.oldObject_ = this.copyObject(this.value_);
|
| +
|
| + this.report_([
|
| + diff.added || {},
|
| + diff.removed || {},
|
| + diff.changed || {},
|
| + function(property) {
|
| + return oldValues[property];
|
| + }
|
| + ]);
|
| +
|
| + return true;
|
| + },
|
| +
|
| + disconnect_: function() {
|
| + if (hasObserve) {
|
| + this.directObserver_.close();
|
| + this.directObserver_ = undefined;
|
| + } else {
|
| + this.oldObject_ = undefined;
|
| + }
|
| + },
|
| +
|
| + deliver: function() {
|
| + if (this.state_ != OPENED)
|
| + return;
|
| +
|
| + if (hasObserve)
|
| + this.directObserver_.deliver(false);
|
| + else
|
| + dirtyCheck(this);
|
| + },
|
| +
|
| + discardChanges: function() {
|
| + if (this.directObserver_)
|
| + this.directObserver_.deliver(true);
|
| + else
|
| + this.oldObject_ = this.copyObject(this.value_);
|
| +
|
| + return this.value_;
|
| + }
|
| + });
|
| +
|
| + function ArrayObserver(array) {
|
| + if (!Array.isArray(array))
|
| + throw Error('Provided object is not an Array');
|
| + ObjectObserver.call(this, array);
|
| + }
|
| +
|
| + ArrayObserver.prototype = createObject({
|
| +
|
| + __proto__: ObjectObserver.prototype,
|
| +
|
| + arrayObserve: true,
|
| +
|
| + copyObject: function(arr) {
|
| + return arr.slice();
|
| + },
|
| +
|
| + check_: function(changeRecords) {
|
| + var splices;
|
| + if (hasObserve) {
|
| + if (!changeRecords)
|
| + return false;
|
| + splices = projectArraySplices(this.value_, changeRecords);
|
| + } else {
|
| + splices = calcSplices(this.value_, 0, this.value_.length,
|
| + this.oldObject_, 0, this.oldObject_.length);
|
| + }
|
| +
|
| + if (!splices || !splices.length)
|
| + return false;
|
| +
|
| + if (!hasObserve)
|
| + this.oldObject_ = this.copyObject(this.value_);
|
| +
|
| + this.report_([splices]);
|
| + return true;
|
| + }
|
| + });
|
| +
|
| + ArrayObserver.applySplices = function(previous, current, splices) {
|
| + splices.forEach(function(splice) {
|
| + var spliceArgs = [splice.index, splice.removed.length];
|
| + var addIndex = splice.index;
|
| + while (addIndex < splice.index + splice.addedCount) {
|
| + spliceArgs.push(current[addIndex]);
|
| + addIndex++;
|
| + }
|
| +
|
| + Array.prototype.splice.apply(previous, spliceArgs);
|
| + });
|
| + };
|
| +
|
| + function PathObserver(object, path) {
|
| + Observer.call(this);
|
| +
|
| + this.object_ = object;
|
| + this.path_ = getPath(path);
|
| + this.directObserver_ = undefined;
|
| + }
|
| +
|
| + PathObserver.prototype = createObject({
|
| + __proto__: Observer.prototype,
|
| +
|
| + get path() {
|
| + return this.path_;
|
| + },
|
| +
|
| + connect_: function() {
|
| + if (hasObserve)
|
| + this.directObserver_ = getObservedSet(this, this.object_);
|
| +
|
| + this.check_(undefined, true);
|
| + },
|
| +
|
| + disconnect_: function() {
|
| + this.value_ = undefined;
|
| +
|
| + if (this.directObserver_) {
|
| + this.directObserver_.close(this);
|
| + this.directObserver_ = undefined;
|
| + }
|
| + },
|
| +
|
| + iterateObjects_: function(observe) {
|
| + this.path_.iterateObjects(this.object_, observe);
|
| + },
|
| +
|
| + check_: function(changeRecords, skipChanges) {
|
| + var oldValue = this.value_;
|
| + this.value_ = this.path_.getValueFrom(this.object_);
|
| + if (skipChanges || areSameValue(this.value_, oldValue))
|
| + return false;
|
| +
|
| + this.report_([this.value_, oldValue, this]);
|
| + return true;
|
| + },
|
| +
|
| + setValue: function(newValue) {
|
| + if (this.path_)
|
| + this.path_.setValueFrom(this.object_, newValue);
|
| + }
|
| + });
|
| +
|
| + function CompoundObserver(reportChangesOnOpen) {
|
| + Observer.call(this);
|
| +
|
| + this.reportChangesOnOpen_ = reportChangesOnOpen;
|
| + this.value_ = [];
|
| + this.directObserver_ = undefined;
|
| + this.observed_ = [];
|
| + }
|
| +
|
| + var observerSentinel = {};
|
| +
|
| + CompoundObserver.prototype = createObject({
|
| + __proto__: Observer.prototype,
|
| +
|
| + connect_: function() {
|
| + if (hasObserve) {
|
| + var object;
|
| + var needsDirectObserver = false;
|
| + for (var i = 0; i < this.observed_.length; i += 2) {
|
| + object = this.observed_[i]
|
| + if (object !== observerSentinel) {
|
| + needsDirectObserver = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (needsDirectObserver)
|
| + this.directObserver_ = getObservedSet(this, object);
|
| + }
|
| +
|
| + this.check_(undefined, !this.reportChangesOnOpen_);
|
| + },
|
| +
|
| + disconnect_: function() {
|
| + for (var i = 0; i < this.observed_.length; i += 2) {
|
| + if (this.observed_[i] === observerSentinel)
|
| + this.observed_[i + 1].close();
|
| + }
|
| + this.observed_.length = 0;
|
| + this.value_.length = 0;
|
| +
|
| + if (this.directObserver_) {
|
| + this.directObserver_.close(this);
|
| + this.directObserver_ = undefined;
|
| + }
|
| + },
|
| +
|
| + addPath: function(object, path) {
|
| + if (this.state_ != UNOPENED && this.state_ != RESETTING)
|
| + throw Error('Cannot add paths once started.');
|
| +
|
| + var path = getPath(path);
|
| + this.observed_.push(object, path);
|
| + if (!this.reportChangesOnOpen_)
|
| + return;
|
| + var index = this.observed_.length / 2 - 1;
|
| + this.value_[index] = path.getValueFrom(object);
|
| + },
|
| +
|
| + addObserver: function(observer) {
|
| + if (this.state_ != UNOPENED && this.state_ != RESETTING)
|
| + throw Error('Cannot add observers once started.');
|
| +
|
| + this.observed_.push(observerSentinel, observer);
|
| + if (!this.reportChangesOnOpen_)
|
| + return;
|
| + var index = this.observed_.length / 2 - 1;
|
| + this.value_[index] = observer.open(this.deliver, this);
|
| + },
|
| +
|
| + startReset: function() {
|
| + if (this.state_ != OPENED)
|
| + throw Error('Can only reset while open');
|
| +
|
| + this.state_ = RESETTING;
|
| + this.disconnect_();
|
| + },
|
| +
|
| + finishReset: function() {
|
| + if (this.state_ != RESETTING)
|
| + throw Error('Can only finishReset after startReset');
|
| + this.state_ = OPENED;
|
| + this.connect_();
|
| +
|
| + return this.value_;
|
| + },
|
| +
|
| + iterateObjects_: function(observe) {
|
| + var object;
|
| + for (var i = 0; i < this.observed_.length; i += 2) {
|
| + object = this.observed_[i]
|
| + if (object !== observerSentinel)
|
| + this.observed_[i + 1].iterateObjects(object, observe)
|
| + }
|
| + },
|
| +
|
| + check_: function(changeRecords, skipChanges) {
|
| + var oldValues;
|
| + for (var i = 0; i < this.observed_.length; i += 2) {
|
| + var object = this.observed_[i];
|
| + var path = this.observed_[i+1];
|
| + var value;
|
| + if (object === observerSentinel) {
|
| + var observable = path;
|
| + value = this.state_ === UNOPENED ?
|
| + observable.open(this.deliver, this) :
|
| + observable.discardChanges();
|
| + } else {
|
| + value = path.getValueFrom(object);
|
| + }
|
| +
|
| + if (skipChanges) {
|
| + this.value_[i / 2] = value;
|
| + continue;
|
| + }
|
| +
|
| + if (areSameValue(value, this.value_[i / 2]))
|
| + continue;
|
| +
|
| + oldValues = oldValues || [];
|
| + oldValues[i / 2] = this.value_[i / 2];
|
| + this.value_[i / 2] = value;
|
| + }
|
| +
|
| + if (!oldValues)
|
| + return false;
|
| +
|
| + // TODO(rafaelw): Having observed_ as the third callback arg here is
|
| + // pretty lame API. Fix.
|
| + this.report_([this.value_, oldValues, this.observed_]);
|
| + return true;
|
| + }
|
| + });
|
| +
|
| + function identFn(value) { return value; }
|
| +
|
| + function ObserverTransform(observable, getValueFn, setValueFn,
|
| + dontPassThroughSet) {
|
| + this.callback_ = undefined;
|
| + this.target_ = undefined;
|
| + this.value_ = undefined;
|
| + this.observable_ = observable;
|
| + this.getValueFn_ = getValueFn || identFn;
|
| + this.setValueFn_ = setValueFn || identFn;
|
| + // TODO(rafaelw): This is a temporary hack. PolymerExpressions needs this
|
| + // at the moment because of a bug in it's dependency tracking.
|
| + this.dontPassThroughSet_ = dontPassThroughSet;
|
| + }
|
| +
|
| + ObserverTransform.prototype = {
|
| + open: function(callback, target) {
|
| + this.callback_ = callback;
|
| + this.target_ = target;
|
| + this.value_ =
|
| + this.getValueFn_(this.observable_.open(this.observedCallback_, this));
|
| + return this.value_;
|
| + },
|
| +
|
| + observedCallback_: function(value) {
|
| + value = this.getValueFn_(value);
|
| + if (areSameValue(value, this.value_))
|
| + return;
|
| + var oldValue = this.value_;
|
| + this.value_ = value;
|
| + this.callback_.call(this.target_, this.value_, oldValue);
|
| + },
|
| +
|
| + discardChanges: function() {
|
| + this.value_ = this.getValueFn_(this.observable_.discardChanges());
|
| + return this.value_;
|
| + },
|
| +
|
| + deliver: function() {
|
| + return this.observable_.deliver();
|
| + },
|
| +
|
| + setValue: function(value) {
|
| + value = this.setValueFn_(value);
|
| + if (!this.dontPassThroughSet_ && this.observable_.setValue)
|
| + return this.observable_.setValue(value);
|
| + },
|
| +
|
| + close: function() {
|
| + if (this.observable_)
|
| + this.observable_.close();
|
| + this.callback_ = undefined;
|
| + this.target_ = undefined;
|
| + this.observable_ = undefined;
|
| + this.value_ = undefined;
|
| + this.getValueFn_ = undefined;
|
| + this.setValueFn_ = undefined;
|
| + }
|
| + }
|
| +
|
| + var expectedRecordTypes = {
|
| + add: true,
|
| + update: true,
|
| + delete: true
|
| + };
|
| +
|
| + function diffObjectFromChangeRecords(object, changeRecords, oldValues) {
|
| + var added = {};
|
| + var removed = {};
|
| +
|
| + for (var i = 0; i < changeRecords.length; i++) {
|
| + var record = changeRecords[i];
|
| + if (!expectedRecordTypes[record.type]) {
|
| + console.error('Unknown changeRecord type: ' + record.type);
|
| + console.error(record);
|
| + continue;
|
| + }
|
| +
|
| + if (!(record.name in oldValues))
|
| + oldValues[record.name] = record.oldValue;
|
| +
|
| + if (record.type == 'update')
|
| + continue;
|
| +
|
| + if (record.type == 'add') {
|
| + if (record.name in removed)
|
| + delete removed[record.name];
|
| + else
|
| + added[record.name] = true;
|
| +
|
| + continue;
|
| + }
|
| +
|
| + // type = 'delete'
|
| + if (record.name in added) {
|
| + delete added[record.name];
|
| + delete oldValues[record.name];
|
| + } else {
|
| + removed[record.name] = true;
|
| + }
|
| + }
|
| +
|
| + for (var prop in added)
|
| + added[prop] = object[prop];
|
| +
|
| + for (var prop in removed)
|
| + removed[prop] = undefined;
|
| +
|
| + var changed = {};
|
| + for (var prop in oldValues) {
|
| + if (prop in added || prop in removed)
|
| + continue;
|
| +
|
| + var newValue = object[prop];
|
| + if (oldValues[prop] !== newValue)
|
| + changed[prop] = newValue;
|
| + }
|
| +
|
| + return {
|
| + added: added,
|
| + removed: removed,
|
| + changed: changed
|
| + };
|
| + }
|
| +
|
| + function newSplice(index, removed, addedCount) {
|
| + return {
|
| + index: index,
|
| + removed: removed,
|
| + addedCount: addedCount
|
| + };
|
| + }
|
| +
|
| + var EDIT_LEAVE = 0;
|
| + var EDIT_UPDATE = 1;
|
| + var EDIT_ADD = 2;
|
| + var EDIT_DELETE = 3;
|
| +
|
| + function ArraySplice() {}
|
| +
|
| + ArraySplice.prototype = {
|
| +
|
| + // Note: This function is *based* on the computation of the Levenshtein
|
| + // "edit" distance. The one change is that "updates" are treated as two
|
| + // edits - not one. With Array splices, an update is really a delete
|
| + // followed by an add. By retaining this, we optimize for "keeping" the
|
| + // maximum array items in the original array. For example:
|
| + //
|
| + // 'xxxx123' -> '123yyyy'
|
| + //
|
| + // With 1-edit updates, the shortest path would be just to update all seven
|
| + // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
| + // leaves the substring '123' intact.
|
| + calcEditDistances: function(current, currentStart, currentEnd,
|
| + old, oldStart, oldEnd) {
|
| + // "Deletion" columns
|
| + var rowCount = oldEnd - oldStart + 1;
|
| + var columnCount = currentEnd - currentStart + 1;
|
| + var distances = new Array(rowCount);
|
| +
|
| + // "Addition" rows. Initialize null column.
|
| + for (var i = 0; i < rowCount; i++) {
|
| + distances[i] = new Array(columnCount);
|
| + distances[i][0] = i;
|
| + }
|
| +
|
| + // Initialize null row
|
| + for (var j = 0; j < columnCount; j++)
|
| + distances[0][j] = j;
|
| +
|
| + for (var i = 1; i < rowCount; i++) {
|
| + for (var j = 1; j < columnCount; j++) {
|
| + if (this.equals(current[currentStart + j - 1], old[oldStart + i - 1]))
|
| + distances[i][j] = distances[i - 1][j - 1];
|
| + else {
|
| + var north = distances[i - 1][j] + 1;
|
| + var west = distances[i][j - 1] + 1;
|
| + distances[i][j] = north < west ? north : west;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return distances;
|
| + },
|
| +
|
| + // This starts at the final weight, and walks "backward" by finding
|
| + // the minimum previous weight recursively until the origin of the weight
|
| + // matrix.
|
| + spliceOperationsFromEditDistances: function(distances) {
|
| + var i = distances.length - 1;
|
| + var j = distances[0].length - 1;
|
| + var current = distances[i][j];
|
| + var edits = [];
|
| + while (i > 0 || j > 0) {
|
| + if (i == 0) {
|
| + edits.push(EDIT_ADD);
|
| + j--;
|
| + continue;
|
| + }
|
| + if (j == 0) {
|
| + edits.push(EDIT_DELETE);
|
| + i--;
|
| + continue;
|
| + }
|
| + var northWest = distances[i - 1][j - 1];
|
| + var west = distances[i - 1][j];
|
| + var north = distances[i][j - 1];
|
| +
|
| + var min;
|
| + if (west < north)
|
| + min = west < northWest ? west : northWest;
|
| + else
|
| + min = north < northWest ? north : northWest;
|
| +
|
| + if (min == northWest) {
|
| + if (northWest == current) {
|
| + edits.push(EDIT_LEAVE);
|
| + } else {
|
| + edits.push(EDIT_UPDATE);
|
| + current = northWest;
|
| + }
|
| + i--;
|
| + j--;
|
| + } else if (min == west) {
|
| + edits.push(EDIT_DELETE);
|
| + i--;
|
| + current = west;
|
| + } else {
|
| + edits.push(EDIT_ADD);
|
| + j--;
|
| + current = north;
|
| + }
|
| + }
|
| +
|
| + edits.reverse();
|
| + return edits;
|
| + },
|
| +
|
| + /**
|
| + * Splice Projection functions:
|
| + *
|
| + * A splice map is a representation of how a previous array of items
|
| + * was transformed into a new array of items. Conceptually it is a list of
|
| + * tuples of
|
| + *
|
| + * <index, removed, addedCount>
|
| + *
|
| + * which are kept in ascending index order of. The tuple represents that at
|
| + * the |index|, |removed| sequence of items were removed, and counting forward
|
| + * from |index|, |addedCount| items were added.
|
| + */
|
| +
|
| + /**
|
| + * Lacking individual splice mutation information, the minimal set of
|
| + * splices can be synthesized given the previous state and final state of an
|
| + * array. The basic approach is to calculate the edit distance matrix and
|
| + * choose the shortest path through it.
|
| + *
|
| + * Complexity: O(l * p)
|
| + * l: The length of the current array
|
| + * p: The length of the old array
|
| + */
|
| + calcSplices: function(current, currentStart, currentEnd,
|
| + old, oldStart, oldEnd) {
|
| + var prefixCount = 0;
|
| + var suffixCount = 0;
|
| +
|
| + var minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
| + if (currentStart == 0 && oldStart == 0)
|
| + prefixCount = this.sharedPrefix(current, old, minLength);
|
| +
|
| + if (currentEnd == current.length && oldEnd == old.length)
|
| + suffixCount = this.sharedSuffix(current, old, minLength - prefixCount);
|
| +
|
| + currentStart += prefixCount;
|
| + oldStart += prefixCount;
|
| + currentEnd -= suffixCount;
|
| + oldEnd -= suffixCount;
|
| +
|
| + if (currentEnd - currentStart == 0 && oldEnd - oldStart == 0)
|
| + return [];
|
| +
|
| + if (currentStart == currentEnd) {
|
| + var splice = newSplice(currentStart, [], 0);
|
| + while (oldStart < oldEnd)
|
| + splice.removed.push(old[oldStart++]);
|
| +
|
| + return [ splice ];
|
| + } else if (oldStart == oldEnd)
|
| + return [ newSplice(currentStart, [], currentEnd - currentStart) ];
|
| +
|
| + var ops = this.spliceOperationsFromEditDistances(
|
| + this.calcEditDistances(current, currentStart, currentEnd,
|
| + old, oldStart, oldEnd));
|
| +
|
| + var splice = undefined;
|
| + var splices = [];
|
| + var index = currentStart;
|
| + var oldIndex = oldStart;
|
| + for (var i = 0; i < ops.length; i++) {
|
| + switch(ops[i]) {
|
| + case EDIT_LEAVE:
|
| + if (splice) {
|
| + splices.push(splice);
|
| + splice = undefined;
|
| + }
|
| +
|
| + index++;
|
| + oldIndex++;
|
| + break;
|
| + case EDIT_UPDATE:
|
| + if (!splice)
|
| + splice = newSplice(index, [], 0);
|
| +
|
| + splice.addedCount++;
|
| + index++;
|
| +
|
| + splice.removed.push(old[oldIndex]);
|
| + oldIndex++;
|
| + break;
|
| + case EDIT_ADD:
|
| + if (!splice)
|
| + splice = newSplice(index, [], 0);
|
| +
|
| + splice.addedCount++;
|
| + index++;
|
| + break;
|
| + case EDIT_DELETE:
|
| + if (!splice)
|
| + splice = newSplice(index, [], 0);
|
| +
|
| + splice.removed.push(old[oldIndex]);
|
| + oldIndex++;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + if (splice) {
|
| + splices.push(splice);
|
| + }
|
| + return splices;
|
| + },
|
| +
|
| + sharedPrefix: function(current, old, searchLength) {
|
| + for (var i = 0; i < searchLength; i++)
|
| + if (!this.equals(current[i], old[i]))
|
| + return i;
|
| + return searchLength;
|
| + },
|
| +
|
| + sharedSuffix: function(current, old, searchLength) {
|
| + var index1 = current.length;
|
| + var index2 = old.length;
|
| + var count = 0;
|
| + while (count < searchLength && this.equals(current[--index1], old[--index2]))
|
| + count++;
|
| +
|
| + return count;
|
| + },
|
| +
|
| + calculateSplices: function(current, previous) {
|
| + return this.calcSplices(current, 0, current.length, previous, 0,
|
| + previous.length);
|
| + },
|
| +
|
| + equals: function(currentValue, previousValue) {
|
| + return currentValue === previousValue;
|
| + }
|
| + };
|
| +
|
| + var arraySplice = new ArraySplice();
|
| +
|
| + function calcSplices(current, currentStart, currentEnd,
|
| + old, oldStart, oldEnd) {
|
| + return arraySplice.calcSplices(current, currentStart, currentEnd,
|
| + old, oldStart, oldEnd);
|
| + }
|
| +
|
| + function intersect(start1, end1, start2, end2) {
|
| + // Disjoint
|
| + if (end1 < start2 || end2 < start1)
|
| + return -1;
|
| +
|
| + // Adjacent
|
| + if (end1 == start2 || end2 == start1)
|
| + return 0;
|
| +
|
| + // Non-zero intersect, span1 first
|
| + if (start1 < start2) {
|
| + if (end1 < end2)
|
| + return end1 - start2; // Overlap
|
| + else
|
| + return end2 - start2; // Contained
|
| + } else {
|
| + // Non-zero intersect, span2 first
|
| + if (end2 < end1)
|
| + return end2 - start1; // Overlap
|
| + else
|
| + return end1 - start1; // Contained
|
| + }
|
| + }
|
| +
|
| + function mergeSplice(splices, index, removed, addedCount) {
|
| +
|
| + var splice = newSplice(index, removed, addedCount);
|
| +
|
| + var inserted = false;
|
| + var insertionOffset = 0;
|
| +
|
| + for (var i = 0; i < splices.length; i++) {
|
| + var current = splices[i];
|
| + current.index += insertionOffset;
|
| +
|
| + if (inserted)
|
| + continue;
|
| +
|
| + var intersectCount = intersect(splice.index,
|
| + splice.index + splice.removed.length,
|
| + current.index,
|
| + current.index + current.addedCount);
|
| +
|
| + if (intersectCount >= 0) {
|
| + // Merge the two splices
|
| +
|
| + splices.splice(i, 1);
|
| + i--;
|
| +
|
| + insertionOffset -= current.addedCount - current.removed.length;
|
| +
|
| + splice.addedCount += current.addedCount - intersectCount;
|
| + var deleteCount = splice.removed.length +
|
| + current.removed.length - intersectCount;
|
| +
|
| + if (!splice.addedCount && !deleteCount) {
|
| + // merged splice is a noop. discard.
|
| + inserted = true;
|
| + } else {
|
| + var removed = current.removed;
|
| +
|
| + if (splice.index < current.index) {
|
| + // some prefix of splice.removed is prepended to current.removed.
|
| + var prepend = splice.removed.slice(0, current.index - splice.index);
|
| + Array.prototype.push.apply(prepend, removed);
|
| + removed = prepend;
|
| + }
|
| +
|
| + if (splice.index + splice.removed.length > current.index + current.addedCount) {
|
| + // some suffix of splice.removed is appended to current.removed.
|
| + var append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
| + Array.prototype.push.apply(removed, append);
|
| + }
|
| +
|
| + splice.removed = removed;
|
| + if (current.index < splice.index) {
|
| + splice.index = current.index;
|
| + }
|
| + }
|
| + } else if (splice.index < current.index) {
|
| + // Insert splice here.
|
| +
|
| + inserted = true;
|
| +
|
| + splices.splice(i, 0, splice);
|
| + i++;
|
| +
|
| + var offset = splice.addedCount - splice.removed.length
|
| + current.index += offset;
|
| + insertionOffset += offset;
|
| + }
|
| + }
|
| +
|
| + if (!inserted)
|
| + splices.push(splice);
|
| + }
|
| +
|
| + function createInitialSplices(array, changeRecords) {
|
| + var splices = [];
|
| +
|
| + for (var i = 0; i < changeRecords.length; i++) {
|
| + var record = changeRecords[i];
|
| + switch(record.type) {
|
| + case 'splice':
|
| + mergeSplice(splices, record.index, record.removed.slice(), record.addedCount);
|
| + break;
|
| + case 'add':
|
| + case 'update':
|
| + case 'delete':
|
| + if (!isIndex(record.name))
|
| + continue;
|
| + var index = toNumber(record.name);
|
| + if (index < 0)
|
| + continue;
|
| + mergeSplice(splices, index, [record.oldValue], 1);
|
| + break;
|
| + default:
|
| + console.error('Unexpected record type: ' + JSON.stringify(record));
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return splices;
|
| + }
|
| +
|
| + function projectArraySplices(array, changeRecords) {
|
| + var splices = [];
|
| +
|
| + createInitialSplices(array, changeRecords).forEach(function(splice) {
|
| + if (splice.addedCount == 1 && splice.removed.length == 1) {
|
| + if (splice.removed[0] !== array[splice.index])
|
| + splices.push(splice);
|
| +
|
| + return
|
| + };
|
| +
|
| + splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount,
|
| + splice.removed, 0, splice.removed.length));
|
| + });
|
| +
|
| + return splices;
|
| + }
|
| +
|
| + // Export the observe-js object for **Node.js**, with
|
| + // backwards-compatibility for the old `require()` API. If we're in
|
| + // the browser, export as a global object.
|
| +
|
| + var expose = global;
|
| +
|
| + if (typeof exports !== 'undefined') {
|
| + if (typeof module !== 'undefined' && module.exports) {
|
| + expose = exports = module.exports;
|
| + }
|
| + expose = exports;
|
| + }
|
| +
|
| + expose.Observer = Observer;
|
| + expose.Observer.runEOM_ = runEOM;
|
| + expose.Observer.observerSentinel_ = observerSentinel; // for testing.
|
| + expose.Observer.hasObjectObserve = hasObserve;
|
| + expose.ArrayObserver = ArrayObserver;
|
| + expose.ArrayObserver.calculateSplices = function(current, previous) {
|
| + return arraySplice.calculateSplices(current, previous);
|
| + };
|
| +
|
| + expose.ArraySplice = ArraySplice;
|
| + expose.ObjectObserver = ObjectObserver;
|
| + expose.PathObserver = PathObserver;
|
| + expose.CompoundObserver = CompoundObserver;
|
| + expose.Path = Path;
|
| + expose.ObserverTransform = ObserverTransform;
|
| +
|
| +})(typeof global !== 'undefined' && global && typeof module !== 'undefined' && module ? global : this || window);
|
| +
|
| +// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
| +// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +// Code distributed by Google as part of the polymer project is also
|
| +// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +
|
| +(function(global) {
|
| + 'use strict';
|
| +
|
| + var filter = Array.prototype.filter.call.bind(Array.prototype.filter);
|
| +
|
| + function getTreeScope(node) {
|
| + while (node.parentNode) {
|
| + node = node.parentNode;
|
| + }
|
| +
|
| + return typeof node.getElementById === 'function' ? node : null;
|
| + }
|
| +
|
| + Node.prototype.bind = function(name, observable) {
|
| + console.error('Unhandled binding to Node: ', this, name, observable);
|
| + };
|
| +
|
| + Node.prototype.bindFinished = function() {};
|
| +
|
| + function updateBindings(node, name, binding) {
|
| + var bindings = node.bindings_;
|
| + if (!bindings)
|
| + bindings = node.bindings_ = {};
|
| +
|
| + if (bindings[name])
|
| + binding[name].close();
|
| +
|
| + return bindings[name] = binding;
|
| + }
|
| +
|
| + function returnBinding(node, name, binding) {
|
| + return binding;
|
| + }
|
| +
|
| + function sanitizeValue(value) {
|
| + return value == null ? '' : value;
|
| + }
|
| +
|
| + function updateText(node, value) {
|
| + node.data = sanitizeValue(value);
|
| + }
|
| +
|
| + function textBinding(node) {
|
| + return function(value) {
|
| + return updateText(node, value);
|
| + };
|
| + }
|
| +
|
| + var maybeUpdateBindings = returnBinding;
|
| +
|
| + Object.defineProperty(Platform, 'enableBindingsReflection', {
|
| + get: function() {
|
| + return maybeUpdateBindings === updateBindings;
|
| + },
|
| + set: function(enable) {
|
| + maybeUpdateBindings = enable ? updateBindings : returnBinding;
|
| + return enable;
|
| + },
|
| + configurable: true
|
| + });
|
| +
|
| + Text.prototype.bind = function(name, value, oneTime) {
|
| + if (name !== 'textContent')
|
| + return Node.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + if (oneTime)
|
| + return updateText(this, value);
|
| +
|
| + var observable = value;
|
| + updateText(this, observable.open(textBinding(this)));
|
| + return maybeUpdateBindings(this, name, observable);
|
| + }
|
| +
|
| + function updateAttribute(el, name, conditional, value) {
|
| + if (conditional) {
|
| + if (value)
|
| + el.setAttribute(name, '');
|
| + else
|
| + el.removeAttribute(name);
|
| + return;
|
| + }
|
| +
|
| + el.setAttribute(name, sanitizeValue(value));
|
| + }
|
| +
|
| + function attributeBinding(el, name, conditional) {
|
| + return function(value) {
|
| + updateAttribute(el, name, conditional, value);
|
| + };
|
| + }
|
| +
|
| + Element.prototype.bind = function(name, value, oneTime) {
|
| + var conditional = name[name.length - 1] == '?';
|
| + if (conditional) {
|
| + this.removeAttribute(name);
|
| + name = name.slice(0, -1);
|
| + }
|
| +
|
| + if (oneTime)
|
| + return updateAttribute(this, name, conditional, value);
|
| +
|
| +
|
| + var observable = value;
|
| + updateAttribute(this, name, conditional,
|
| + observable.open(attributeBinding(this, name, conditional)));
|
| +
|
| + return maybeUpdateBindings(this, name, observable);
|
| + };
|
| +
|
| + var checkboxEventType;
|
| + (function() {
|
| + // Attempt to feature-detect which event (change or click) is fired first
|
| + // for checkboxes.
|
| + var div = document.createElement('div');
|
| + var checkbox = div.appendChild(document.createElement('input'));
|
| + checkbox.setAttribute('type', 'checkbox');
|
| + var first;
|
| + var count = 0;
|
| + checkbox.addEventListener('click', function(e) {
|
| + count++;
|
| + first = first || 'click';
|
| + });
|
| + checkbox.addEventListener('change', function() {
|
| + count++;
|
| + first = first || 'change';
|
| + });
|
| +
|
| + var event = document.createEvent('MouseEvent');
|
| + event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false,
|
| + false, false, false, 0, null);
|
| + checkbox.dispatchEvent(event);
|
| + // WebKit/Blink don't fire the change event if the element is outside the
|
| + // document, so assume 'change' for that case.
|
| + checkboxEventType = count == 1 ? 'change' : first;
|
| + })();
|
| +
|
| + function getEventForInputType(element) {
|
| + switch (element.type) {
|
| + case 'checkbox':
|
| + return checkboxEventType;
|
| + case 'radio':
|
| + case 'select-multiple':
|
| + case 'select-one':
|
| + return 'change';
|
| + case 'range':
|
| + if (/Trident|MSIE/.test(navigator.userAgent))
|
| + return 'change';
|
| + default:
|
| + return 'input';
|
| + }
|
| + }
|
| +
|
| + function updateInput(input, property, value, santizeFn) {
|
| + input[property] = (santizeFn || sanitizeValue)(value);
|
| + }
|
| +
|
| + function inputBinding(input, property, santizeFn) {
|
| + return function(value) {
|
| + return updateInput(input, property, value, santizeFn);
|
| + }
|
| + }
|
| +
|
| + function noop() {}
|
| +
|
| + function bindInputEvent(input, property, observable, postEventFn) {
|
| + var eventType = getEventForInputType(input);
|
| +
|
| + function eventHandler() {
|
| + observable.setValue(input[property]);
|
| + observable.discardChanges();
|
| + (postEventFn || noop)(input);
|
| + Platform.performMicrotaskCheckpoint();
|
| + }
|
| + input.addEventListener(eventType, eventHandler);
|
| +
|
| + return {
|
| + close: function() {
|
| + input.removeEventListener(eventType, eventHandler);
|
| + observable.close();
|
| + },
|
| +
|
| + observable_: observable
|
| + }
|
| + }
|
| +
|
| + function booleanSanitize(value) {
|
| + return Boolean(value);
|
| + }
|
| +
|
| + // |element| is assumed to be an HTMLInputElement with |type| == 'radio'.
|
| + // Returns an array containing all radio buttons other than |element| that
|
| + // have the same |name|, either in the form that |element| belongs to or,
|
| + // if no form, in the document tree to which |element| belongs.
|
| + //
|
| + // This implementation is based upon the HTML spec definition of a
|
| + // "radio button group":
|
| + // http://www.whatwg.org/specs/web-apps/current-work/multipage/number-state.html#radio-button-group
|
| + //
|
| + function getAssociatedRadioButtons(element) {
|
| + if (element.form) {
|
| + return filter(element.form.elements, function(el) {
|
| + return el != element &&
|
| + el.tagName == 'INPUT' &&
|
| + el.type == 'radio' &&
|
| + el.name == element.name;
|
| + });
|
| + } else {
|
| + var treeScope = getTreeScope(element);
|
| + if (!treeScope)
|
| + return [];
|
| + var radios = treeScope.querySelectorAll(
|
| + 'input[type="radio"][name="' + element.name + '"]');
|
| + return filter(radios, function(el) {
|
| + return el != element && !el.form;
|
| + });
|
| + }
|
| + }
|
| +
|
| + function checkedPostEvent(input) {
|
| + // Only the radio button that is getting checked gets an event. We
|
| + // therefore find all the associated radio buttons and update their
|
| + // check binding manually.
|
| + if (input.tagName === 'INPUT' &&
|
| + input.type === 'radio') {
|
| + getAssociatedRadioButtons(input).forEach(function(radio) {
|
| + var checkedBinding = radio.bindings_.checked;
|
| + if (checkedBinding) {
|
| + // Set the value directly to avoid an infinite call stack.
|
| + checkedBinding.observable_.setValue(false);
|
| + }
|
| + });
|
| + }
|
| + }
|
| +
|
| + HTMLInputElement.prototype.bind = function(name, value, oneTime) {
|
| + if (name !== 'value' && name !== 'checked')
|
| + return HTMLElement.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + this.removeAttribute(name);
|
| + var sanitizeFn = name == 'checked' ? booleanSanitize : sanitizeValue;
|
| + var postEventFn = name == 'checked' ? checkedPostEvent : noop;
|
| +
|
| + if (oneTime)
|
| + return updateInput(this, name, value, sanitizeFn);
|
| +
|
| +
|
| + var observable = value;
|
| + var binding = bindInputEvent(this, name, observable, postEventFn);
|
| + updateInput(this, name,
|
| + observable.open(inputBinding(this, name, sanitizeFn)),
|
| + sanitizeFn);
|
| +
|
| + // Checkboxes may need to update bindings of other checkboxes.
|
| + return updateBindings(this, name, binding);
|
| + }
|
| +
|
| + HTMLTextAreaElement.prototype.bind = function(name, value, oneTime) {
|
| + if (name !== 'value')
|
| + return HTMLElement.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + this.removeAttribute('value');
|
| +
|
| + if (oneTime)
|
| + return updateInput(this, 'value', value);
|
| +
|
| + var observable = value;
|
| + var binding = bindInputEvent(this, 'value', observable);
|
| + updateInput(this, 'value',
|
| + observable.open(inputBinding(this, 'value', sanitizeValue)));
|
| + return maybeUpdateBindings(this, name, binding);
|
| + }
|
| +
|
| + function updateOption(option, value) {
|
| + var parentNode = option.parentNode;;
|
| + var select;
|
| + var selectBinding;
|
| + var oldValue;
|
| + if (parentNode instanceof HTMLSelectElement &&
|
| + parentNode.bindings_ &&
|
| + parentNode.bindings_.value) {
|
| + select = parentNode;
|
| + selectBinding = select.bindings_.value;
|
| + oldValue = select.value;
|
| + }
|
| +
|
| + option.value = sanitizeValue(value);
|
| +
|
| + if (select && select.value != oldValue) {
|
| + selectBinding.observable_.setValue(select.value);
|
| + selectBinding.observable_.discardChanges();
|
| + Platform.performMicrotaskCheckpoint();
|
| + }
|
| + }
|
| +
|
| + function optionBinding(option) {
|
| + return function(value) {
|
| + updateOption(option, value);
|
| + }
|
| + }
|
| +
|
| + HTMLOptionElement.prototype.bind = function(name, value, oneTime) {
|
| + if (name !== 'value')
|
| + return HTMLElement.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + this.removeAttribute('value');
|
| +
|
| + if (oneTime)
|
| + return updateOption(this, value);
|
| +
|
| + var observable = value;
|
| + var binding = bindInputEvent(this, 'value', observable);
|
| + updateOption(this, observable.open(optionBinding(this)));
|
| + return maybeUpdateBindings(this, name, binding);
|
| + }
|
| +
|
| + HTMLSelectElement.prototype.bind = function(name, value, oneTime) {
|
| + if (name === 'selectedindex')
|
| + name = 'selectedIndex';
|
| +
|
| + if (name !== 'selectedIndex' && name !== 'value')
|
| + return HTMLElement.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + this.removeAttribute(name);
|
| +
|
| + if (oneTime)
|
| + return updateInput(this, name, value);
|
| +
|
| + var observable = value;
|
| + var binding = bindInputEvent(this, name, observable);
|
| + updateInput(this, name,
|
| + observable.open(inputBinding(this, name)));
|
| +
|
| + // Option update events may need to access select bindings.
|
| + return updateBindings(this, name, binding);
|
| + }
|
| +})(this);
|
| +
|
| +// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
| +// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
| +// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
| +// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
| +// Code distributed by Google as part of the polymer project is also
|
| +// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
| +
|
| +(function(global) {
|
| + 'use strict';
|
| +
|
| + function assert(v) {
|
| + if (!v)
|
| + throw new Error('Assertion failed');
|
| + }
|
| +
|
| + var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
|
| +
|
| + function getFragmentRoot(node) {
|
| + var p;
|
| + while (p = node.parentNode) {
|
| + node = p;
|
| + }
|
| +
|
| + return node;
|
| + }
|
| +
|
| + function searchRefId(node, id) {
|
| + if (!id)
|
| + return;
|
| +
|
| + var ref;
|
| + var selector = '#' + id;
|
| + while (!ref) {
|
| + node = getFragmentRoot(node);
|
| +
|
| + if (node.protoContent_)
|
| + ref = node.protoContent_.querySelector(selector);
|
| + else if (node.getElementById)
|
| + ref = node.getElementById(id);
|
| +
|
| + if (ref || !node.templateCreator_)
|
| + break
|
| +
|
| + node = node.templateCreator_;
|
| + }
|
| +
|
| + return ref;
|
| + }
|
| +
|
| + function getInstanceRoot(node) {
|
| + while (node.parentNode) {
|
| + node = node.parentNode;
|
| + }
|
| + return node.templateCreator_ ? node : null;
|
| + }
|
| +
|
| + var Map;
|
| + if (global.Map && typeof global.Map.prototype.forEach === 'function') {
|
| + Map = global.Map;
|
| + } else {
|
| + Map = function() {
|
| + this.keys = [];
|
| + this.values = [];
|
| + };
|
| +
|
| + Map.prototype = {
|
| + set: function(key, value) {
|
| + var index = this.keys.indexOf(key);
|
| + if (index < 0) {
|
| + this.keys.push(key);
|
| + this.values.push(value);
|
| + } else {
|
| + this.values[index] = value;
|
| + }
|
| + },
|
| +
|
| + get: function(key) {
|
| + var index = this.keys.indexOf(key);
|
| + if (index < 0)
|
| + return;
|
| +
|
| + return this.values[index];
|
| + },
|
| +
|
| + delete: function(key, value) {
|
| + var index = this.keys.indexOf(key);
|
| + if (index < 0)
|
| + return false;
|
| +
|
| + this.keys.splice(index, 1);
|
| + this.values.splice(index, 1);
|
| + return true;
|
| + },
|
| +
|
| + forEach: function(f, opt_this) {
|
| + for (var i = 0; i < this.keys.length; i++)
|
| + f.call(opt_this || this, this.values[i], this.keys[i], this);
|
| + }
|
| + };
|
| + }
|
| +
|
| + // JScript does not have __proto__. We wrap all object literals with
|
| + // createObject which uses Object.create, Object.defineProperty and
|
| + // Object.getOwnPropertyDescriptor to create a new object that does the exact
|
| + // same thing. The main downside to this solution is that we have to extract
|
| + // all those property descriptors for IE.
|
| + var createObject = ('__proto__' in {}) ?
|
| + function(obj) { return obj; } :
|
| + function(obj) {
|
| + var proto = obj.__proto__;
|
| + if (!proto)
|
| + return obj;
|
| + var newObject = Object.create(proto);
|
| + Object.getOwnPropertyNames(obj).forEach(function(name) {
|
| + Object.defineProperty(newObject, name,
|
| + Object.getOwnPropertyDescriptor(obj, name));
|
| + });
|
| + return newObject;
|
| + };
|
| +
|
| + // IE does not support have Document.prototype.contains.
|
| + if (typeof document.contains != 'function') {
|
| + Document.prototype.contains = function(node) {
|
| + if (node === this || node.parentNode === this)
|
| + return true;
|
| + return this.documentElement.contains(node);
|
| + }
|
| + }
|
| +
|
| + var BIND = 'bind';
|
| + var REPEAT = 'repeat';
|
| + var IF = 'if';
|
| +
|
| + var templateAttributeDirectives = {
|
| + 'template': true,
|
| + 'repeat': true,
|
| + 'bind': true,
|
| + 'ref': true
|
| + };
|
| +
|
| + var semanticTemplateElements = {
|
| + 'THEAD': true,
|
| + 'TBODY': true,
|
| + 'TFOOT': true,
|
| + 'TH': true,
|
| + 'TR': true,
|
| + 'TD': true,
|
| + 'COLGROUP': true,
|
| + 'COL': true,
|
| + 'CAPTION': true,
|
| + 'OPTION': true,
|
| + 'OPTGROUP': true
|
| + };
|
| +
|
| + var hasTemplateElement = typeof HTMLTemplateElement !== 'undefined';
|
| + if (hasTemplateElement) {
|
| + // TODO(rafaelw): Remove when fix for
|
| + // https://codereview.chromium.org/164803002/
|
| + // makes it to Chrome release.
|
| + (function() {
|
| + var t = document.createElement('template');
|
| + var d = t.content.ownerDocument;
|
| + var html = d.appendChild(d.createElement('html'));
|
| + var head = html.appendChild(d.createElement('head'));
|
| + var base = d.createElement('base');
|
| + base.href = document.baseURI;
|
| + head.appendChild(base);
|
| + })();
|
| + }
|
| +
|
| + var allTemplatesSelectors = 'template, ' +
|
| + Object.keys(semanticTemplateElements).map(function(tagName) {
|
| + return tagName.toLowerCase() + '[template]';
|
| + }).join(', ');
|
| +
|
| + function isSVGTemplate(el) {
|
| + return el.tagName == 'template' &&
|
| + el.namespaceURI == 'http://www.w3.org/2000/svg';
|
| + }
|
| +
|
| + function isHTMLTemplate(el) {
|
| + return el.tagName == 'TEMPLATE' &&
|
| + el.namespaceURI == 'http://www.w3.org/1999/xhtml';
|
| + }
|
| +
|
| + function isAttributeTemplate(el) {
|
| + return Boolean(semanticTemplateElements[el.tagName] &&
|
| + el.hasAttribute('template'));
|
| + }
|
| +
|
| + function isTemplate(el) {
|
| + if (el.isTemplate_ === undefined)
|
| + el.isTemplate_ = el.tagName == 'TEMPLATE' || isAttributeTemplate(el);
|
| +
|
| + return el.isTemplate_;
|
| + }
|
| +
|
| + // FIXME: Observe templates being added/removed from documents
|
| + // FIXME: Expose imperative API to decorate and observe templates in
|
| + // "disconnected tress" (e.g. ShadowRoot)
|
| + document.addEventListener('DOMContentLoaded', function(e) {
|
| + bootstrapTemplatesRecursivelyFrom(document);
|
| + // FIXME: Is this needed? Seems like it shouldn't be.
|
| + Platform.performMicrotaskCheckpoint();
|
| + }, false);
|
| +
|
| + function forAllTemplatesFrom(node, fn) {
|
| + var subTemplates = node.querySelectorAll(allTemplatesSelectors);
|
| +
|
| + if (isTemplate(node))
|
| + fn(node)
|
| + forEach(subTemplates, fn);
|
| + }
|
| +
|
| + function bootstrapTemplatesRecursivelyFrom(node) {
|
| + function bootstrap(template) {
|
| + if (!HTMLTemplateElement.decorate(template))
|
| + bootstrapTemplatesRecursivelyFrom(template.content);
|
| + }
|
| +
|
| + forAllTemplatesFrom(node, bootstrap);
|
| + }
|
| +
|
| + if (!hasTemplateElement) {
|
| + /**
|
| + * This represents a <template> element.
|
| + * @constructor
|
| + * @extends {HTMLElement}
|
| + */
|
| + global.HTMLTemplateElement = function() {
|
| + throw TypeError('Illegal constructor');
|
| + };
|
| + }
|
| +
|
| + var hasProto = '__proto__' in {};
|
| +
|
| + function mixin(to, from) {
|
| + Object.getOwnPropertyNames(from).forEach(function(name) {
|
| + Object.defineProperty(to, name,
|
| + Object.getOwnPropertyDescriptor(from, name));
|
| + });
|
| + }
|
| +
|
| + // http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/templates/index.html#dfn-template-contents-owner
|
| + function getOrCreateTemplateContentsOwner(template) {
|
| + var doc = template.ownerDocument
|
| + if (!doc.defaultView)
|
| + return doc;
|
| + var d = doc.templateContentsOwner_;
|
| + if (!d) {
|
| + // TODO(arv): This should either be a Document or HTMLDocument depending
|
| + // on doc.
|
| + d = doc.implementation.createHTMLDocument('');
|
| + while (d.lastChild) {
|
| + d.removeChild(d.lastChild);
|
| + }
|
| + doc.templateContentsOwner_ = d;
|
| + }
|
| + return d;
|
| + }
|
| +
|
| + function getTemplateStagingDocument(template) {
|
| + if (!template.stagingDocument_) {
|
| + var owner = template.ownerDocument;
|
| + if (!owner.stagingDocument_) {
|
| + owner.stagingDocument_ = owner.implementation.createHTMLDocument('');
|
| + owner.stagingDocument_.isStagingDocument = true;
|
| + // TODO(rafaelw): Remove when fix for
|
| + // https://codereview.chromium.org/164803002/
|
| + // makes it to Chrome release.
|
| + var base = owner.stagingDocument_.createElement('base');
|
| + base.href = document.baseURI;
|
| + owner.stagingDocument_.head.appendChild(base);
|
| +
|
| + owner.stagingDocument_.stagingDocument_ = owner.stagingDocument_;
|
| + }
|
| +
|
| + template.stagingDocument_ = owner.stagingDocument_;
|
| + }
|
| +
|
| + return template.stagingDocument_;
|
| + }
|
| +
|
| + // For non-template browsers, the parser will disallow <template> in certain
|
| + // locations, so we allow "attribute templates" which combine the template
|
| + // element with the top-level container node of the content, e.g.
|
| + //
|
| + // <tr template repeat="{{ foo }}"" class="bar"><td>Bar</td></tr>
|
| + //
|
| + // becomes
|
| + //
|
| + // <template repeat="{{ foo }}">
|
| + // + #document-fragment
|
| + // + <tr class="bar">
|
| + // + <td>Bar</td>
|
| + //
|
| + function extractTemplateFromAttributeTemplate(el) {
|
| + var template = el.ownerDocument.createElement('template');
|
| + el.parentNode.insertBefore(template, el);
|
| +
|
| + var attribs = el.attributes;
|
| + var count = attribs.length;
|
| + while (count-- > 0) {
|
| + var attrib = attribs[count];
|
| + if (templateAttributeDirectives[attrib.name]) {
|
| + if (attrib.name !== 'template')
|
| + template.setAttribute(attrib.name, attrib.value);
|
| + el.removeAttribute(attrib.name);
|
| + }
|
| + }
|
| +
|
| + return template;
|
| + }
|
| +
|
| + function extractTemplateFromSVGTemplate(el) {
|
| + var template = el.ownerDocument.createElement('template');
|
| + el.parentNode.insertBefore(template, el);
|
| +
|
| + var attribs = el.attributes;
|
| + var count = attribs.length;
|
| + while (count-- > 0) {
|
| + var attrib = attribs[count];
|
| + template.setAttribute(attrib.name, attrib.value);
|
| + el.removeAttribute(attrib.name);
|
| + }
|
| +
|
| + el.parentNode.removeChild(el);
|
| + return template;
|
| + }
|
| +
|
| + function liftNonNativeTemplateChildrenIntoContent(template, el, useRoot) {
|
| + var content = template.content;
|
| + if (useRoot) {
|
| + content.appendChild(el);
|
| + return;
|
| + }
|
| +
|
| + var child;
|
| + while (child = el.firstChild) {
|
| + content.appendChild(child);
|
| + }
|
| + }
|
| +
|
| + var templateObserver;
|
| + if (typeof MutationObserver == 'function') {
|
| + templateObserver = new MutationObserver(function(records) {
|
| + for (var i = 0; i < records.length; i++) {
|
| + records[i].target.refChanged_();
|
| + }
|
| + });
|
| + }
|
| +
|
| + /**
|
| + * Ensures proper API and content model for template elements.
|
| + * @param {HTMLTemplateElement} opt_instanceRef The template element which
|
| + * |el| template element will return as the value of its ref(), and whose
|
| + * content will be used as source when createInstance() is invoked.
|
| + */
|
| + HTMLTemplateElement.decorate = function(el, opt_instanceRef) {
|
| + if (el.templateIsDecorated_)
|
| + return false;
|
| +
|
| + var templateElement = el;
|
| + templateElement.templateIsDecorated_ = true;
|
| +
|
| + var isNativeHTMLTemplate = isHTMLTemplate(templateElement) &&
|
| + hasTemplateElement;
|
| + var bootstrapContents = isNativeHTMLTemplate;
|
| + var liftContents = !isNativeHTMLTemplate;
|
| + var liftRoot = false;
|
| +
|
| + if (!isNativeHTMLTemplate) {
|
| + if (isAttributeTemplate(templateElement)) {
|
| + assert(!opt_instanceRef);
|
| + templateElement = extractTemplateFromAttributeTemplate(el);
|
| + templateElement.templateIsDecorated_ = true;
|
| + isNativeHTMLTemplate = hasTemplateElement;
|
| + liftRoot = true;
|
| + } else if (isSVGTemplate(templateElement)) {
|
| + templateElement = extractTemplateFromSVGTemplate(el);
|
| + templateElement.templateIsDecorated_ = true;
|
| + isNativeHTMLTemplate = hasTemplateElement;
|
| + }
|
| + }
|
| +
|
| + if (!isNativeHTMLTemplate) {
|
| + fixTemplateElementPrototype(templateElement);
|
| + var doc = getOrCreateTemplateContentsOwner(templateElement);
|
| + templateElement.content_ = doc.createDocumentFragment();
|
| + }
|
| +
|
| + if (opt_instanceRef) {
|
| + // template is contained within an instance, its direct content must be
|
| + // empty
|
| + templateElement.instanceRef_ = opt_instanceRef;
|
| + } else if (liftContents) {
|
| + liftNonNativeTemplateChildrenIntoContent(templateElement,
|
| + el,
|
| + liftRoot);
|
| + } else if (bootstrapContents) {
|
| + bootstrapTemplatesRecursivelyFrom(templateElement.content);
|
| + }
|
| +
|
| + return true;
|
| + };
|
| +
|
| + // TODO(rafaelw): This used to decorate recursively all templates from a given
|
| + // node. This happens by default on 'DOMContentLoaded', but may be needed
|
| + // in subtrees not descendent from document (e.g. ShadowRoot).
|
| + // Review whether this is the right public API.
|
| + HTMLTemplateElement.bootstrap = bootstrapTemplatesRecursivelyFrom;
|
| +
|
| + var htmlElement = global.HTMLUnknownElement || HTMLElement;
|
| +
|
| + var contentDescriptor = {
|
| + get: function() {
|
| + return this.content_;
|
| + },
|
| + enumerable: true,
|
| + configurable: true
|
| + };
|
| +
|
| + if (!hasTemplateElement) {
|
| + // Gecko is more picky with the prototype than WebKit. Make sure to use the
|
| + // same prototype as created in the constructor.
|
| + HTMLTemplateElement.prototype = Object.create(htmlElement.prototype);
|
| +
|
| + Object.defineProperty(HTMLTemplateElement.prototype, 'content',
|
| + contentDescriptor);
|
| + }
|
| +
|
| + function fixTemplateElementPrototype(el) {
|
| + if (hasProto)
|
| + el.__proto__ = HTMLTemplateElement.prototype;
|
| + else
|
| + mixin(el, HTMLTemplateElement.prototype);
|
| + }
|
| +
|
| + function ensureSetModelScheduled(template) {
|
| + if (!template.setModelFn_) {
|
| + template.setModelFn_ = function() {
|
| + template.setModelFnScheduled_ = false;
|
| + var map = getBindings(template,
|
| + template.delegate_ && template.delegate_.prepareBinding);
|
| + processBindings(template, map, template.model_);
|
| + };
|
| + }
|
| +
|
| + if (!template.setModelFnScheduled_) {
|
| + template.setModelFnScheduled_ = true;
|
| + Observer.runEOM_(template.setModelFn_);
|
| + }
|
| + }
|
| +
|
| + mixin(HTMLTemplateElement.prototype, {
|
| + bind: function(name, value, oneTime) {
|
| + if (name != 'ref')
|
| + return Element.prototype.bind.call(this, name, value, oneTime);
|
| +
|
| + var self = this;
|
| + var ref = oneTime ? value : value.open(function(ref) {
|
| + self.setAttribute('ref', ref);
|
| + self.refChanged_();
|
| + });
|
| +
|
| + this.setAttribute('ref', ref);
|
| + this.refChanged_();
|
| + if (oneTime)
|
| + return;
|
| +
|
| + if (!this.bindings_) {
|
| + this.bindings_ = { ref: value };
|
| + } else {
|
| + this.bindings_.ref = value;
|
| + }
|
| +
|
| + return value;
|
| + },
|
| +
|
| + processBindingDirectives_: function(directives) {
|
| + if (this.iterator_)
|
| + this.iterator_.closeDeps();
|
| +
|
| + if (!directives.if && !directives.bind && !directives.repeat) {
|
| + if (this.iterator_) {
|
| + this.iterator_.close();
|
| + this.iterator_ = undefined;
|
| + }
|
| +
|
| + return;
|
| + }
|
| +
|
| + if (!this.iterator_) {
|
| + this.iterator_ = new TemplateIterator(this);
|
| + }
|
| +
|
| + this.iterator_.updateDependencies(directives, this.model_);
|
| +
|
| + if (templateObserver) {
|
| + templateObserver.observe(this, { attributes: true,
|
| + attributeFilter: ['ref'] });
|
| + }
|
| +
|
| + return this.iterator_;
|
| + },
|
| +
|
| + createInstance: function(model, bindingDelegate, delegate_) {
|
| + if (bindingDelegate)
|
| + delegate_ = this.newDelegate_(bindingDelegate);
|
| + else if (!delegate_)
|
| + delegate_ = this.delegate_;
|
| +
|
| + if (!this.refContent_)
|
| + this.refContent_ = this.ref_.content;
|
| + var content = this.refContent_;
|
| + if (content.firstChild === null)
|
| + return emptyInstance;
|
| +
|
| + var map = getInstanceBindingMap(content, delegate_);
|
| + var stagingDocument = getTemplateStagingDocument(this);
|
| + var instance = stagingDocument.createDocumentFragment();
|
| + instance.templateCreator_ = this;
|
| + instance.protoContent_ = content;
|
| + instance.bindings_ = [];
|
| + instance.terminator_ = null;
|
| + var instanceRecord = instance.templateInstance_ = {
|
| + firstNode: null,
|
| + lastNode: null,
|
| + model: model
|
| + };
|
| +
|
| + var i = 0;
|
| + var collectTerminator = false;
|
| + for (var child = content.firstChild; child; child = child.nextSibling) {
|
| + // The terminator of the instance is the clone of the last child of the
|
| + // content. If the last child is an active template, it may produce
|
| + // instances as a result of production, so simply collecting the last
|
| + // child of the instance after it has finished producing may be wrong.
|
| + if (child.nextSibling === null)
|
| + collectTerminator = true;
|
| +
|
| + var clone = cloneAndBindInstance(child, instance, stagingDocument,
|
| + map.children[i++],
|
| + model,
|
| + delegate_,
|
| + instance.bindings_);
|
| + clone.templateInstance_ = instanceRecord;
|
| + if (collectTerminator)
|
| + instance.terminator_ = clone;
|
| + }
|
| +
|
| + instanceRecord.firstNode = instance.firstChild;
|
| + instanceRecord.lastNode = instance.lastChild;
|
| + instance.templateCreator_ = undefined;
|
| + instance.protoContent_ = undefined;
|
| + return instance;
|
| + },
|
| +
|
| + get model() {
|
| + return this.model_;
|
| + },
|
| +
|
| + set model(model) {
|
| + this.model_ = model;
|
| + ensureSetModelScheduled(this);
|
| + },
|
| +
|
| + get bindingDelegate() {
|
| + return this.delegate_ && this.delegate_.raw;
|
| + },
|
| +
|
| + refChanged_: function() {
|
| + if (!this.iterator_ || this.refContent_ === this.ref_.content)
|
| + return;
|
| +
|
| + this.refContent_ = undefined;
|
| + this.iterator_.valueChanged();
|
| + this.iterator_.updateIteratedValue(this.iterator_.getUpdatedValue());
|
| + },
|
| +
|
| + clear: function() {
|
| + this.model_ = undefined;
|
| + this.delegate_ = undefined;
|
| + if (this.bindings_ && this.bindings_.ref)
|
| + this.bindings_.ref.close()
|
| + this.refContent_ = undefined;
|
| + if (!this.iterator_)
|
| + return;
|
| + this.iterator_.valueChanged();
|
| + this.iterator_.close()
|
| + this.iterator_ = undefined;
|
| + },
|
| +
|
| + setDelegate_: function(delegate) {
|
| + this.delegate_ = delegate;
|
| + this.bindingMap_ = undefined;
|
| + if (this.iterator_) {
|
| + this.iterator_.instancePositionChangedFn_ = undefined;
|
| + this.iterator_.instanceModelFn_ = undefined;
|
| + }
|
| + },
|
| +
|
| + newDelegate_: function(bindingDelegate) {
|
| + if (!bindingDelegate)
|
| + return;
|
| +
|
| + function delegateFn(name) {
|
| + var fn = bindingDelegate && bindingDelegate[name];
|
| + if (typeof fn != 'function')
|
| + return;
|
| +
|
| + return function() {
|
| + return fn.apply(bindingDelegate, arguments);
|
| + };
|
| + }
|
| +
|
| + return {
|
| + bindingMaps: {},
|
| + raw: bindingDelegate,
|
| + prepareBinding: delegateFn('prepareBinding'),
|
| + prepareInstanceModel: delegateFn('prepareInstanceModel'),
|
| + prepareInstancePositionChanged:
|
| + delegateFn('prepareInstancePositionChanged')
|
| + };
|
| + },
|
| +
|
| + set bindingDelegate(bindingDelegate) {
|
| + if (this.delegate_) {
|
| + throw Error('Template must be cleared before a new bindingDelegate ' +
|
| + 'can be assigned');
|
| + }
|
| +
|
| + this.setDelegate_(this.newDelegate_(bindingDelegate));
|
| + },
|
| +
|
| + get ref_() {
|
| + var ref = searchRefId(this, this.getAttribute('ref'));
|
| + if (!ref)
|
| + ref = this.instanceRef_;
|
| +
|
| + if (!ref)
|
| + return this;
|
| +
|
| + var nextRef = ref.ref_;
|
| + return nextRef ? nextRef : ref;
|
| + }
|
| + });
|
| +
|
| + // Returns
|
| + // a) undefined if there are no mustaches.
|
| + // b) [TEXT, (ONE_TIME?, PATH, DELEGATE_FN, TEXT)+] if there is at least one mustache.
|
| + function parseMustaches(s, name, node, prepareBindingFn) {
|
| + if (!s || !s.length)
|
| + return;
|
| +
|
| + var tokens;
|
| + var length = s.length;
|
| + var startIndex = 0, lastIndex = 0, endIndex = 0;
|
| + var onlyOneTime = true;
|
| + while (lastIndex < length) {
|
| + var startIndex = s.indexOf('{{', lastIndex);
|
| + var oneTimeStart = s.indexOf('[[', lastIndex);
|
| + var oneTime = false;
|
| + var terminator = '}}';
|
| +
|
| + if (oneTimeStart >= 0 &&
|
| + (startIndex < 0 || oneTimeStart < startIndex)) {
|
| + startIndex = oneTimeStart;
|
| + oneTime = true;
|
| + terminator = ']]';
|
| + }
|
| +
|
| + endIndex = startIndex < 0 ? -1 : s.indexOf(terminator, startIndex + 2);
|
| +
|
| + if (endIndex < 0) {
|
| + if (!tokens)
|
| + return;
|
| +
|
| + tokens.push(s.slice(lastIndex)); // TEXT
|
| + break;
|
| + }
|
| +
|
| + tokens = tokens || [];
|
| + tokens.push(s.slice(lastIndex, startIndex)); // TEXT
|
| + var pathString = s.slice(startIndex + 2, endIndex).trim();
|
| + tokens.push(oneTime); // ONE_TIME?
|
| + onlyOneTime = onlyOneTime && oneTime;
|
| + var delegateFn = prepareBindingFn &&
|
| + prepareBindingFn(pathString, name, node);
|
| + // Don't try to parse the expression if there's a prepareBinding function
|
| + if (delegateFn == null) {
|
| + tokens.push(Path.get(pathString)); // PATH
|
| + } else {
|
| + tokens.push(null);
|
| + }
|
| + tokens.push(delegateFn); // DELEGATE_FN
|
| + lastIndex = endIndex + 2;
|
| + }
|
| +
|
| + if (lastIndex === length)
|
| + tokens.push(''); // TEXT
|
| +
|
| + tokens.hasOnePath = tokens.length === 5;
|
| + tokens.isSimplePath = tokens.hasOnePath &&
|
| + tokens[0] == '' &&
|
| + tokens[4] == '';
|
| + tokens.onlyOneTime = onlyOneTime;
|
| +
|
| + tokens.combinator = function(values) {
|
| + var newValue = tokens[0];
|
| +
|
| + for (var i = 1; i < tokens.length; i += 4) {
|
| + var value = tokens.hasOnePath ? values : values[(i - 1) / 4];
|
| + if (value !== undefined)
|
| + newValue += value;
|
| + newValue += tokens[i + 3];
|
| + }
|
| +
|
| + return newValue;
|
| + }
|
| +
|
| + return tokens;
|
| + };
|
| +
|
| + function processOneTimeBinding(name, tokens, node, model) {
|
| + if (tokens.hasOnePath) {
|
| + var delegateFn = tokens[3];
|
| + var value = delegateFn ? delegateFn(model, node, true) :
|
| + tokens[2].getValueFrom(model);
|
| + return tokens.isSimplePath ? value : tokens.combinator(value);
|
| + }
|
| +
|
| + var values = [];
|
| + for (var i = 1; i < tokens.length; i += 4) {
|
| + var delegateFn = tokens[i + 2];
|
| + values[(i - 1) / 4] = delegateFn ? delegateFn(model, node) :
|
| + tokens[i + 1].getValueFrom(model);
|
| + }
|
| +
|
| + return tokens.combinator(values);
|
| + }
|
| +
|
| + function processSinglePathBinding(name, tokens, node, model) {
|
| + var delegateFn = tokens[3];
|
| + var observer = delegateFn ? delegateFn(model, node, false) :
|
| + new PathObserver(model, tokens[2]);
|
| +
|
| + return tokens.isSimplePath ? observer :
|
| + new ObserverTransform(observer, tokens.combinator);
|
| + }
|
| +
|
| + function processBinding(name, tokens, node, model) {
|
| + if (tokens.onlyOneTime)
|
| + return processOneTimeBinding(name, tokens, node, model);
|
| +
|
| + if (tokens.hasOnePath)
|
| + return processSinglePathBinding(name, tokens, node, model);
|
| +
|
| + var observer = new CompoundObserver();
|
| +
|
| + for (var i = 1; i < tokens.length; i += 4) {
|
| + var oneTime = tokens[i];
|
| + var delegateFn = tokens[i + 2];
|
| +
|
| + if (delegateFn) {
|
| + var value = delegateFn(model, node, oneTime);
|
| + if (oneTime)
|
| + observer.addPath(value)
|
| + else
|
| + observer.addObserver(value);
|
| + continue;
|
| + }
|
| +
|
| + var path = tokens[i + 1];
|
| + if (oneTime)
|
| + observer.addPath(path.getValueFrom(model))
|
| + else
|
| + observer.addPath(model, path);
|
| + }
|
| +
|
| + return new ObserverTransform(observer, tokens.combinator);
|
| + }
|
| +
|
| + function processBindings(node, bindings, model, instanceBindings) {
|
| + for (var i = 0; i < bindings.length; i += 2) {
|
| + var name = bindings[i]
|
| + var tokens = bindings[i + 1];
|
| + var value = processBinding(name, tokens, node, model);
|
| + var binding = node.bind(name, value, tokens.onlyOneTime);
|
| + if (binding && instanceBindings)
|
| + instanceBindings.push(binding);
|
| + }
|
| +
|
| + node.bindFinished();
|
| + if (!bindings.isTemplate)
|
| + return;
|
| +
|
| + node.model_ = model;
|
| + var iter = node.processBindingDirectives_(bindings);
|
| + if (instanceBindings && iter)
|
| + instanceBindings.push(iter);
|
| + }
|
| +
|
| + function parseWithDefault(el, name, prepareBindingFn) {
|
| + var v = el.getAttribute(name);
|
| + return parseMustaches(v == '' ? '{{}}' : v, name, el, prepareBindingFn);
|
| + }
|
| +
|
| + function parseAttributeBindings(element, prepareBindingFn) {
|
| + assert(element);
|
| +
|
| + var bindings = [];
|
| + var ifFound = false;
|
| + var bindFound = false;
|
| +
|
| + for (var i = 0; i < element.attributes.length; i++) {
|
| + var attr = element.attributes[i];
|
| + var name = attr.name;
|
| + var value = attr.value;
|
| +
|
| + // Allow bindings expressed in attributes to be prefixed with underbars.
|
| + // We do this to allow correct semantics for browsers that don't implement
|
| + // <template> where certain attributes might trigger side-effects -- and
|
| + // for IE which sanitizes certain attributes, disallowing mustache
|
| + // replacements in their text.
|
| + while (name[0] === '_') {
|
| + name = name.substring(1);
|
| + }
|
| +
|
| + if (isTemplate(element) &&
|
| + (name === IF || name === BIND || name === REPEAT)) {
|
| + continue;
|
| + }
|
| +
|
| + var tokens = parseMustaches(value, name, element,
|
| + prepareBindingFn);
|
| + if (!tokens)
|
| + continue;
|
| +
|
| + bindings.push(name, tokens);
|
| + }
|
| +
|
| + if (isTemplate(element)) {
|
| + bindings.isTemplate = true;
|
| + bindings.if = parseWithDefault(element, IF, prepareBindingFn);
|
| + bindings.bind = parseWithDefault(element, BIND, prepareBindingFn);
|
| + bindings.repeat = parseWithDefault(element, REPEAT, prepareBindingFn);
|
| +
|
| + if (bindings.if && !bindings.bind && !bindings.repeat)
|
| + bindings.bind = parseMustaches('{{}}', BIND, element, prepareBindingFn);
|
| + }
|
| +
|
| + return bindings;
|
| + }
|
| +
|
| + function getBindings(node, prepareBindingFn) {
|
| + if (node.nodeType === Node.ELEMENT_NODE)
|
| + return parseAttributeBindings(node, prepareBindingFn);
|
| +
|
| + if (node.nodeType === Node.TEXT_NODE) {
|
| + var tokens = parseMustaches(node.data, 'textContent', node,
|
| + prepareBindingFn);
|
| + if (tokens)
|
| + return ['textContent', tokens];
|
| + }
|
| +
|
| + return [];
|
| + }
|
| +
|
| + function cloneAndBindInstance(node, parent, stagingDocument, bindings, model,
|
| + delegate,
|
| + instanceBindings,
|
| + instanceRecord) {
|
| + var clone = parent.appendChild(stagingDocument.importNode(node, false));
|
| +
|
| + var i = 0;
|
| + for (var child = node.firstChild; child; child = child.nextSibling) {
|
| + cloneAndBindInstance(child, clone, stagingDocument,
|
| + bindings.children[i++],
|
| + model,
|
| + delegate,
|
| + instanceBindings);
|
| + }
|
| +
|
| + if (bindings.isTemplate) {
|
| + HTMLTemplateElement.decorate(clone, node);
|
| + if (delegate)
|
| + clone.setDelegate_(delegate);
|
| + }
|
| +
|
| + processBindings(clone, bindings, model, instanceBindings);
|
| + return clone;
|
| + }
|
| +
|
| + function createInstanceBindingMap(node, prepareBindingFn) {
|
| + var map = getBindings(node, prepareBindingFn);
|
| + map.children = {};
|
| + var index = 0;
|
| + for (var child = node.firstChild; child; child = child.nextSibling) {
|
| + map.children[index++] = createInstanceBindingMap(child, prepareBindingFn);
|
| + }
|
| +
|
| + return map;
|
| + }
|
| +
|
| + var contentUidCounter = 1;
|
| +
|
| + // TODO(rafaelw): Setup a MutationObserver on content which clears the id
|
| + // so that bindingMaps regenerate when the template.content changes.
|
| + function getContentUid(content) {
|
| + var id = content.id_;
|
| + if (!id)
|
| + id = content.id_ = contentUidCounter++;
|
| + return id;
|
| + }
|
| +
|
| + // Each delegate is associated with a set of bindingMaps, one for each
|
| + // content which may be used by a template. The intent is that each binding
|
| + // delegate gets the opportunity to prepare the instance (via the prepare*
|
| + // delegate calls) once across all uses.
|
| + // TODO(rafaelw): Separate out the parse map from the binding map. In the
|
| + // current implementation, if two delegates need a binding map for the same
|
| + // content, the second will have to reparse.
|
| + function getInstanceBindingMap(content, delegate_) {
|
| + var contentId = getContentUid(content);
|
| + if (delegate_) {
|
| + var map = delegate_.bindingMaps[contentId];
|
| + if (!map) {
|
| + map = delegate_.bindingMaps[contentId] =
|
| + createInstanceBindingMap(content, delegate_.prepareBinding) || [];
|
| + }
|
| + return map;
|
| + }
|
| +
|
| + var map = content.bindingMap_;
|
| + if (!map) {
|
| + map = content.bindingMap_ =
|
| + createInstanceBindingMap(content, undefined) || [];
|
| + }
|
| + return map;
|
| + }
|
| +
|
| + Object.defineProperty(Node.prototype, 'templateInstance', {
|
| + get: function() {
|
| + var instance = this.templateInstance_;
|
| + return instance ? instance :
|
| + (this.parentNode ? this.parentNode.templateInstance : undefined);
|
| + }
|
| + });
|
| +
|
| + var emptyInstance = document.createDocumentFragment();
|
| + emptyInstance.bindings_ = [];
|
| + emptyInstance.terminator_ = null;
|
| +
|
| + function TemplateIterator(templateElement) {
|
| + this.closed = false;
|
| + this.templateElement_ = templateElement;
|
| + this.instances = [];
|
| + this.deps = undefined;
|
| + this.iteratedValue = [];
|
| + this.presentValue = undefined;
|
| + this.arrayObserver = undefined;
|
| + }
|
| +
|
| + TemplateIterator.prototype = {
|
| + closeDeps: function() {
|
| + var deps = this.deps;
|
| + if (deps) {
|
| + if (deps.ifOneTime === false)
|
| + deps.ifValue.close();
|
| + if (deps.oneTime === false)
|
| + deps.value.close();
|
| + }
|
| + },
|
| +
|
| + updateDependencies: function(directives, model) {
|
| + this.closeDeps();
|
| +
|
| + var deps = this.deps = {};
|
| + var template = this.templateElement_;
|
| +
|
| + var ifValue = true;
|
| + if (directives.if) {
|
| + deps.hasIf = true;
|
| + deps.ifOneTime = directives.if.onlyOneTime;
|
| + deps.ifValue = processBinding(IF, directives.if, template, model);
|
| +
|
| + ifValue = deps.ifValue;
|
| +
|
| + // oneTime if & predicate is false. nothing else to do.
|
| + if (deps.ifOneTime && !ifValue) {
|
| + this.valueChanged();
|
| + return;
|
| + }
|
| +
|
| + if (!deps.ifOneTime)
|
| + ifValue = ifValue.open(this.updateIfValue, this);
|
| + }
|
| +
|
| + if (directives.repeat) {
|
| + deps.repeat = true;
|
| + deps.oneTime = directives.repeat.onlyOneTime;
|
| + deps.value = processBinding(REPEAT, directives.repeat, template, model);
|
| + } else {
|
| + deps.repeat = false;
|
| + deps.oneTime = directives.bind.onlyOneTime;
|
| + deps.value = processBinding(BIND, directives.bind, template, model);
|
| + }
|
| +
|
| + var value = deps.value;
|
| + if (!deps.oneTime)
|
| + value = value.open(this.updateIteratedValue, this);
|
| +
|
| + if (!ifValue) {
|
| + this.valueChanged();
|
| + return;
|
| + }
|
| +
|
| + this.updateValue(value);
|
| + },
|
| +
|
| + /**
|
| + * Gets the updated value of the bind/repeat. This can potentially call
|
| + * user code (if a bindingDelegate is set up) so we try to avoid it if we
|
| + * already have the value in hand (from Observer.open).
|
| + */
|
| + getUpdatedValue: function() {
|
| + var value = this.deps.value;
|
| + if (!this.deps.oneTime)
|
| + value = value.discardChanges();
|
| + return value;
|
| + },
|
| +
|
| + updateIfValue: function(ifValue) {
|
| + if (!ifValue) {
|
| + this.valueChanged();
|
| + return;
|
| + }
|
| +
|
| + this.updateValue(this.getUpdatedValue());
|
| + },
|
| +
|
| + updateIteratedValue: function(value) {
|
| + if (this.deps.hasIf) {
|
| + var ifValue = this.deps.ifValue;
|
| + if (!this.deps.ifOneTime)
|
| + ifValue = ifValue.discardChanges();
|
| + if (!ifValue) {
|
| + this.valueChanged();
|
| + return;
|
| + }
|
| + }
|
| +
|
| + this.updateValue(value);
|
| + },
|
| +
|
| + updateValue: function(value) {
|
| + if (!this.deps.repeat)
|
| + value = [value];
|
| + var observe = this.deps.repeat &&
|
| + !this.deps.oneTime &&
|
| + Array.isArray(value);
|
| + this.valueChanged(value, observe);
|
| + },
|
| +
|
| + valueChanged: function(value, observeValue) {
|
| + if (!Array.isArray(value))
|
| + value = [];
|
| +
|
| + if (value === this.iteratedValue)
|
| + return;
|
| +
|
| + this.unobserve();
|
| + this.presentValue = value;
|
| + if (observeValue) {
|
| + this.arrayObserver = new ArrayObserver(this.presentValue);
|
| + this.arrayObserver.open(this.handleSplices, this);
|
| + }
|
| +
|
| + this.handleSplices(ArrayObserver.calculateSplices(this.presentValue,
|
| + this.iteratedValue));
|
| + },
|
| +
|
| + getLastInstanceNode: function(index) {
|
| + if (index == -1)
|
| + return this.templateElement_;
|
| + var instance = this.instances[index];
|
| + var terminator = instance.terminator_;
|
| + if (!terminator)
|
| + return this.getLastInstanceNode(index - 1);
|
| +
|
| + if (terminator.nodeType !== Node.ELEMENT_NODE ||
|
| + this.templateElement_ === terminator) {
|
| + return terminator;
|
| + }
|
| +
|
| + var subtemplateIterator = terminator.iterator_;
|
| + if (!subtemplateIterator)
|
| + return terminator;
|
| +
|
| + return subtemplateIterator.getLastTemplateNode();
|
| + },
|
| +
|
| + getLastTemplateNode: function() {
|
| + return this.getLastInstanceNode(this.instances.length - 1);
|
| + },
|
| +
|
| + insertInstanceAt: function(index, fragment) {
|
| + var previousInstanceLast = this.getLastInstanceNode(index - 1);
|
| + var parent = this.templateElement_.parentNode;
|
| + this.instances.splice(index, 0, fragment);
|
| +
|
| + parent.insertBefore(fragment, previousInstanceLast.nextSibling);
|
| + },
|
| +
|
| + extractInstanceAt: function(index) {
|
| + var previousInstanceLast = this.getLastInstanceNode(index - 1);
|
| + var lastNode = this.getLastInstanceNode(index);
|
| + var parent = this.templateElement_.parentNode;
|
| + var instance = this.instances.splice(index, 1)[0];
|
| +
|
| + while (lastNode !== previousInstanceLast) {
|
| + var node = previousInstanceLast.nextSibling;
|
| + if (node == lastNode)
|
| + lastNode = previousInstanceLast;
|
| +
|
| + instance.appendChild(parent.removeChild(node));
|
| + }
|
| +
|
| + return instance;
|
| + },
|
| +
|
| + getDelegateFn: function(fn) {
|
| + fn = fn && fn(this.templateElement_);
|
| + return typeof fn === 'function' ? fn : null;
|
| + },
|
| +
|
| + handleSplices: function(splices) {
|
| + if (this.closed || !splices.length)
|
| + return;
|
| +
|
| + var template = this.templateElement_;
|
| +
|
| + if (!template.parentNode) {
|
| + this.close();
|
| + return;
|
| + }
|
| +
|
| + ArrayObserver.applySplices(this.iteratedValue, this.presentValue,
|
| + splices);
|
| +
|
| + var delegate = template.delegate_;
|
| + if (this.instanceModelFn_ === undefined) {
|
| + this.instanceModelFn_ =
|
| + this.getDelegateFn(delegate && delegate.prepareInstanceModel);
|
| + }
|
| +
|
| + if (this.instancePositionChangedFn_ === undefined) {
|
| + this.instancePositionChangedFn_ =
|
| + this.getDelegateFn(delegate &&
|
| + delegate.prepareInstancePositionChanged);
|
| + }
|
| +
|
| + // Instance Removals
|
| + var instanceCache = new Map;
|
| + var removeDelta = 0;
|
| + for (var i = 0; i < splices.length; i++) {
|
| + var splice = splices[i];
|
| + var removed = splice.removed;
|
| + for (var j = 0; j < removed.length; j++) {
|
| + var model = removed[j];
|
| + var instance = this.extractInstanceAt(splice.index + removeDelta);
|
| + if (instance !== emptyInstance) {
|
| + instanceCache.set(model, instance);
|
| + }
|
| + }
|
| +
|
| + removeDelta -= splice.addedCount;
|
| + }
|
| +
|
| + // Instance Insertions
|
| + for (var i = 0; i < splices.length; i++) {
|
| + var splice = splices[i];
|
| + var addIndex = splice.index;
|
| + for (; addIndex < splice.index + splice.addedCount; addIndex++) {
|
| + var model = this.iteratedValue[addIndex];
|
| + var instance = instanceCache.get(model);
|
| + if (instance) {
|
| + instanceCache.delete(model);
|
| + } else {
|
| + if (this.instanceModelFn_) {
|
| + model = this.instanceModelFn_(model);
|
| + }
|
| +
|
| + if (model === undefined) {
|
| + instance = emptyInstance;
|
| + } else {
|
| + instance = template.createInstance(model, undefined, delegate);
|
| + }
|
| + }
|
| +
|
| + this.insertInstanceAt(addIndex, instance);
|
| + }
|
| + }
|
| +
|
| + instanceCache.forEach(function(instance) {
|
| + this.closeInstanceBindings(instance);
|
| + }, this);
|
| +
|
| + if (this.instancePositionChangedFn_)
|
| + this.reportInstancesMoved(splices);
|
| + },
|
| +
|
| + reportInstanceMoved: function(index) {
|
| + var instance = this.instances[index];
|
| + if (instance === emptyInstance)
|
| + return;
|
| +
|
| + this.instancePositionChangedFn_(instance.templateInstance_, index);
|
| + },
|
| +
|
| + reportInstancesMoved: function(splices) {
|
| + var index = 0;
|
| + var offset = 0;
|
| + for (var i = 0; i < splices.length; i++) {
|
| + var splice = splices[i];
|
| + if (offset != 0) {
|
| + while (index < splice.index) {
|
| + this.reportInstanceMoved(index);
|
| + index++;
|
| + }
|
| + } else {
|
| + index = splice.index;
|
| + }
|
| +
|
| + while (index < splice.index + splice.addedCount) {
|
| + this.reportInstanceMoved(index);
|
| + index++;
|
| + }
|
| +
|
| + offset += splice.addedCount - splice.removed.length;
|
| + }
|
| +
|
| + if (offset == 0)
|
| + return;
|
| +
|
| + var length = this.instances.length;
|
| + while (index < length) {
|
| + this.reportInstanceMoved(index);
|
| + index++;
|
| + }
|
| + },
|
| +
|
| + closeInstanceBindings: function(instance) {
|
| + var bindings = instance.bindings_;
|
| + for (var i = 0; i < bindings.length; i++) {
|
| + bindings[i].close();
|
| + }
|
| + },
|
| +
|
| + unobserve: function() {
|
| + if (!this.arrayObserver)
|
| + return;
|
| +
|
| + this.arrayObserver.close();
|
| + this.arrayObserver = undefined;
|
| + },
|
| +
|
| + close: function() {
|
| + if (this.closed)
|
| + return;
|
| + this.unobserve();
|
| + for (var i = 0; i < this.instances.length; i++) {
|
| + this.closeInstanceBindings(this.instances[i]);
|
| + }
|
| +
|
| + this.instances.length = 0;
|
| + this.closeDeps();
|
| + this.templateElement_.iterator_ = undefined;
|
| + this.closed = true;
|
| + }
|
| + };
|
| +
|
| + // Polyfill-specific API.
|
| + HTMLTemplateElement.forAllTemplatesFrom_ = forAllTemplatesFrom;
|
| +})(this);
|
| +
|
| +(function(scope) {
|
| + 'use strict';
|
| +
|
| + // feature detect for URL constructor
|
| + var hasWorkingUrl = false;
|
| + if (!scope.forceJURL) {
|
| + try {
|
| + var u = new URL('b', 'http://a');
|
| + hasWorkingUrl = u.href === 'http://a/b';
|
| + } catch(e) {}
|
| + }
|
| +
|
| + if (hasWorkingUrl)
|
| + return;
|
| +
|
| + var relative = Object.create(null);
|
| + relative['ftp'] = 21;
|
| + relative['file'] = 0;
|
| + relative['gopher'] = 70;
|
| + relative['http'] = 80;
|
| + relative['https'] = 443;
|
| + relative['ws'] = 80;
|
| + relative['wss'] = 443;
|
| +
|
| + var relativePathDotMapping = Object.create(null);
|
| + relativePathDotMapping['%2e'] = '.';
|
| + relativePathDotMapping['.%2e'] = '..';
|
| + relativePathDotMapping['%2e.'] = '..';
|
| + relativePathDotMapping['%2e%2e'] = '..';
|
| +
|
| + function isRelativeScheme(scheme) {
|
| + return relative[scheme] !== undefined;
|
| + }
|
| +
|
| + function invalid() {
|
| + clear.call(this);
|
| + this._isInvalid = true;
|
| + }
|
| +
|
| + function IDNAToASCII(h) {
|
| + if ('' == h) {
|
| + invalid.call(this)
|
| + }
|
| + // XXX
|
| + return h.toLowerCase()
|
| + }
|
| +
|
| + function percentEscape(c) {
|
| + var unicode = c.charCodeAt(0);
|
| + if (unicode > 0x20 &&
|
| + unicode < 0x7F &&
|
| + // " # < > ? `
|
| + [0x22, 0x23, 0x3C, 0x3E, 0x3F, 0x60].indexOf(unicode) == -1
|
| + ) {
|
| + return c;
|
| + }
|
| + return encodeURIComponent(c);
|
| + }
|
| +
|
| + function percentEscapeQuery(c) {
|
| + // XXX This actually needs to encode c using encoding and then
|
| + // convert the bytes one-by-one.
|
| +
|
| + var unicode = c.charCodeAt(0);
|
| + if (unicode > 0x20 &&
|
| + unicode < 0x7F &&
|
| + // " # < > ` (do not escape '?')
|
| + [0x22, 0x23, 0x3C, 0x3E, 0x60].indexOf(unicode) == -1
|
| + ) {
|
| + return c;
|
| + }
|
| + return encodeURIComponent(c);
|
| + }
|
| +
|
| + var EOF = undefined,
|
| + ALPHA = /[a-zA-Z]/,
|
| + ALPHANUMERIC = /[a-zA-Z0-9\+\-\.]/;
|
| +
|
| + function parse(input, stateOverride, base) {
|
| + function err(message) {
|
| + errors.push(message)
|
| + }
|
| +
|
| + var state = stateOverride || 'scheme start',
|
| + cursor = 0,
|
| + buffer = '',
|
| + seenAt = false,
|
| + seenBracket = false,
|
| + errors = [];
|
| +
|
| + loop: while ((input[cursor - 1] != EOF || cursor == 0) && !this._isInvalid) {
|
| + var c = input[cursor];
|
| + switch (state) {
|
| + case 'scheme start':
|
| + if (c && ALPHA.test(c)) {
|
| + buffer += c.toLowerCase(); // ASCII-safe
|
| + state = 'scheme';
|
| + } else if (!stateOverride) {
|
| + buffer = '';
|
| + state = 'no scheme';
|
| + continue;
|
| + } else {
|
| + err('Invalid scheme.');
|
| + break loop;
|
| + }
|
| + break;
|
| +
|
| + case 'scheme':
|
| + if (c && ALPHANUMERIC.test(c)) {
|
| + buffer += c.toLowerCase(); // ASCII-safe
|
| + } else if (':' == c) {
|
| + this._scheme = buffer;
|
| + buffer = '';
|
| + if (stateOverride) {
|
| + break loop;
|
| + }
|
| + if (isRelativeScheme(this._scheme)) {
|
| + this._isRelative = true;
|
| + }
|
| + if ('file' == this._scheme) {
|
| + state = 'relative';
|
| + } else if (this._isRelative && base && base._scheme == this._scheme) {
|
| + state = 'relative or authority';
|
| + } else if (this._isRelative) {
|
| + state = 'authority first slash';
|
| + } else {
|
| + state = 'scheme data';
|
| + }
|
| + } else if (!stateOverride) {
|
| + buffer = '';
|
| + cursor = 0;
|
| + state = 'no scheme';
|
| + continue;
|
| + } else if (EOF == c) {
|
| + break loop;
|
| + } else {
|
| + err('Code point not allowed in scheme: ' + c)
|
| + break loop;
|
| + }
|
| + break;
|
| +
|
| + case 'scheme data':
|
| + if ('?' == c) {
|
| + query = '?';
|
| + state = 'query';
|
| + } else if ('#' == c) {
|
| + this._fragment = '#';
|
| + state = 'fragment';
|
| + } else {
|
| + // XXX error handling
|
| + if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
| + this._schemeData += percentEscape(c);
|
| + }
|
| + }
|
| + break;
|
| +
|
| + case 'no scheme':
|
| + if (!base || !(isRelativeScheme(base._scheme))) {
|
| + err('Missing scheme.');
|
| + invalid.call(this);
|
| + } else {
|
| + state = 'relative';
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'relative or authority':
|
| + if ('/' == c && '/' == input[cursor+1]) {
|
| + state = 'authority ignore slashes';
|
| + } else {
|
| + err('Expected /, got: ' + c);
|
| + state = 'relative';
|
| + continue
|
| + }
|
| + break;
|
| +
|
| + case 'relative':
|
| + this._isRelative = true;
|
| + if ('file' != this._scheme)
|
| + this._scheme = base._scheme;
|
| + if (EOF == c) {
|
| + this._host = base._host;
|
| + this._port = base._port;
|
| + this._path = base._path.slice();
|
| + this._query = base._query;
|
| + break loop;
|
| + } else if ('/' == c || '\\' == c) {
|
| + if ('\\' == c)
|
| + err('\\ is an invalid code point.');
|
| + state = 'relative slash';
|
| + } else if ('?' == c) {
|
| + this._host = base._host;
|
| + this._port = base._port;
|
| + this._path = base._path.slice();
|
| + this._query = '?';
|
| + state = 'query';
|
| + } else if ('#' == c) {
|
| + this._host = base._host;
|
| + this._port = base._port;
|
| + this._path = base._path.slice();
|
| + this._query = base._query;
|
| + this._fragment = '#';
|
| + state = 'fragment';
|
| + } else {
|
| + var nextC = input[cursor+1]
|
| + var nextNextC = input[cursor+2]
|
| + if (
|
| + 'file' != this._scheme || !ALPHA.test(c) ||
|
| + (nextC != ':' && nextC != '|') ||
|
| + (EOF != nextNextC && '/' != nextNextC && '\\' != nextNextC && '?' != nextNextC && '#' != nextNextC)) {
|
| + this._host = base._host;
|
| + this._port = base._port;
|
| + this._path = base._path.slice();
|
| + this._path.pop();
|
| + }
|
| + state = 'relative path';
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'relative slash':
|
| + if ('/' == c || '\\' == c) {
|
| + if ('\\' == c) {
|
| + err('\\ is an invalid code point.');
|
| + }
|
| + if ('file' == this._scheme) {
|
| + state = 'file host';
|
| + } else {
|
| + state = 'authority ignore slashes';
|
| + }
|
| + } else {
|
| + if ('file' != this._scheme) {
|
| + this._host = base._host;
|
| + this._port = base._port;
|
| + }
|
| + state = 'relative path';
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'authority first slash':
|
| + if ('/' == c) {
|
| + state = 'authority second slash';
|
| + } else {
|
| + err("Expected '/', got: " + c);
|
| + state = 'authority ignore slashes';
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'authority second slash':
|
| + state = 'authority ignore slashes';
|
| + if ('/' != c) {
|
| + err("Expected '/', got: " + c);
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'authority ignore slashes':
|
| + if ('/' != c && '\\' != c) {
|
| + state = 'authority';
|
| + continue;
|
| + } else {
|
| + err('Expected authority, got: ' + c);
|
| + }
|
| + break;
|
| +
|
| + case 'authority':
|
| + if ('@' == c) {
|
| + if (seenAt) {
|
| + err('@ already seen.');
|
| + buffer += '%40';
|
| + }
|
| + seenAt = true;
|
| + for (var i = 0; i < buffer.length; i++) {
|
| + var cp = buffer[i];
|
| + if ('\t' == cp || '\n' == cp || '\r' == cp) {
|
| + err('Invalid whitespace in authority.');
|
| + continue;
|
| + }
|
| + // XXX check URL code points
|
| + if (':' == cp && null === this._password) {
|
| + this._password = '';
|
| + continue;
|
| + }
|
| + var tempC = percentEscape(cp);
|
| + (null !== this._password) ? this._password += tempC : this._username += tempC;
|
| + }
|
| + buffer = '';
|
| + } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
| + cursor -= buffer.length;
|
| + buffer = '';
|
| + state = 'host';
|
| + continue;
|
| + } else {
|
| + buffer += c;
|
| + }
|
| + break;
|
| +
|
| + case 'file host':
|
| + if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
| + if (buffer.length == 2 && ALPHA.test(buffer[0]) && (buffer[1] == ':' || buffer[1] == '|')) {
|
| + state = 'relative path';
|
| + } else if (buffer.length == 0) {
|
| + state = 'relative path start';
|
| + } else {
|
| + this._host = IDNAToASCII.call(this, buffer);
|
| + buffer = '';
|
| + state = 'relative path start';
|
| + }
|
| + continue;
|
| + } else if ('\t' == c || '\n' == c || '\r' == c) {
|
| + err('Invalid whitespace in file host.');
|
| + } else {
|
| + buffer += c;
|
| + }
|
| + break;
|
| +
|
| + case 'host':
|
| + case 'hostname':
|
| + if (':' == c && !seenBracket) {
|
| + // XXX host parsing
|
| + this._host = IDNAToASCII.call(this, buffer);
|
| + buffer = '';
|
| + state = 'port';
|
| + if ('hostname' == stateOverride) {
|
| + break loop;
|
| + }
|
| + } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c) {
|
| + this._host = IDNAToASCII.call(this, buffer);
|
| + buffer = '';
|
| + state = 'relative path start';
|
| + if (stateOverride) {
|
| + break loop;
|
| + }
|
| + continue;
|
| + } else if ('\t' != c && '\n' != c && '\r' != c) {
|
| + if ('[' == c) {
|
| + seenBracket = true;
|
| + } else if (']' == c) {
|
| + seenBracket = false;
|
| + }
|
| + buffer += c;
|
| + } else {
|
| + err('Invalid code point in host/hostname: ' + c);
|
| + }
|
| + break;
|
| +
|
| + case 'port':
|
| + if (/[0-9]/.test(c)) {
|
| + buffer += c;
|
| + } else if (EOF == c || '/' == c || '\\' == c || '?' == c || '#' == c || stateOverride) {
|
| + if ('' != buffer) {
|
| + var temp = parseInt(buffer, 10);
|
| + if (temp != relative[this._scheme]) {
|
| + this._port = temp + '';
|
| + }
|
| + buffer = '';
|
| + }
|
| + if (stateOverride) {
|
| + break loop;
|
| + }
|
| + state = 'relative path start';
|
| + continue;
|
| + } else if ('\t' == c || '\n' == c || '\r' == c) {
|
| + err('Invalid code point in port: ' + c);
|
| + } else {
|
| + invalid.call(this);
|
| + }
|
| + break;
|
| +
|
| + case 'relative path start':
|
| + if ('\\' == c)
|
| + err("'\\' not allowed in path.");
|
| + state = 'relative path';
|
| + if ('/' != c && '\\' != c) {
|
| + continue;
|
| + }
|
| + break;
|
| +
|
| + case 'relative path':
|
| + if (EOF == c || '/' == c || '\\' == c || (!stateOverride && ('?' == c || '#' == c))) {
|
| + if ('\\' == c) {
|
| + err('\\ not allowed in relative path.');
|
| + }
|
| + var tmp;
|
| + if (tmp = relativePathDotMapping[buffer.toLowerCase()]) {
|
| + buffer = tmp;
|
| + }
|
| + if ('..' == buffer) {
|
| + this._path.pop();
|
| + if ('/' != c && '\\' != c) {
|
| + this._path.push('');
|
| + }
|
| + } else if ('.' == buffer && '/' != c && '\\' != c) {
|
| + this._path.push('');
|
| + } else if ('.' != buffer) {
|
| + if ('file' == this._scheme && this._path.length == 0 && buffer.length == 2 && ALPHA.test(buffer[0]) && buffer[1] == '|') {
|
| + buffer = buffer[0] + ':';
|
| + }
|
| + this._path.push(buffer);
|
| + }
|
| + buffer = '';
|
| + if ('?' == c) {
|
| + this._query = '?';
|
| + state = 'query';
|
| + } else if ('#' == c) {
|
| + this._fragment = '#';
|
| + state = 'fragment';
|
| + }
|
| + } else if ('\t' != c && '\n' != c && '\r' != c) {
|
| + buffer += percentEscape(c);
|
| + }
|
| + break;
|
| +
|
| + case 'query':
|
| + if (!stateOverride && '#' == c) {
|
| + this._fragment = '#';
|
| + state = 'fragment';
|
| + } else if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
| + this._query += percentEscapeQuery(c);
|
| + }
|
| + break;
|
| +
|
| + case 'fragment':
|
| + if (EOF != c && '\t' != c && '\n' != c && '\r' != c) {
|
| + this._fragment += c;
|
| + }
|
| + break;
|
| + }
|
| +
|
| + cursor++;
|
| + }
|
| + }
|
| +
|
| + function clear() {
|
| + this._scheme = '';
|
| + this._schemeData = '';
|
| + this._username = '';
|
| + this._password = null;
|
| + this._host = '';
|
| + this._port = '';
|
| + this._path = [];
|
| + this._query = '';
|
| + this._fragment = '';
|
| + this._isInvalid = false;
|
| + this._isRelative = false;
|
| + }
|
| +
|
| + // Does not process domain names or IP addresses.
|
| + // Does not handle encoding for the query parameter.
|
| + function jURL(url, base /* , encoding */) {
|
| + if (base !== undefined && !(base instanceof jURL))
|
| + base = new jURL(String(base));
|
| +
|
| + this._url = url;
|
| + clear.call(this);
|
| +
|
| + var input = url.replace(/^[ \t\r\n\f]+|[ \t\r\n\f]+$/g, '');
|
| + // encoding = encoding || 'utf-8'
|
| +
|
| + parse.call(this, input, null, base);
|
| + }
|
| +
|
| + jURL.prototype = {
|
| + get href() {
|
| + if (this._isInvalid)
|
| + return this._url;
|
| +
|
| + var authority = '';
|
| + if ('' != this._username || null != this._password) {
|
| + authority = this._username +
|
| + (null != this._password ? ':' + this._password : '') + '@';
|
| + }
|
| +
|
| + return this.protocol +
|
| + (this._isRelative ? '//' + authority + this.host : '') +
|
| + this.pathname + this._query + this._fragment;
|
| + },
|
| + set href(href) {
|
| + clear.call(this);
|
| + parse.call(this, href);
|
| + },
|
| +
|
| + get protocol() {
|
| + return this._scheme + ':';
|
| + },
|
| + set protocol(protocol) {
|
| + if (this._isInvalid)
|
| + return;
|
| + parse.call(this, protocol + ':', 'scheme start');
|
| + },
|
| +
|
| + get host() {
|
| + return this._isInvalid ? '' : this._port ?
|
| + this._host + ':' + this._port : this._host;
|
| + },
|
| + set host(host) {
|
| + if (this._isInvalid || !this._isRelative)
|
| + return;
|
| + parse.call(this, host, 'host');
|
| + },
|
| +
|
| + get hostname() {
|
| + return this._host;
|
| + },
|
| + set hostname(hostname) {
|
| + if (this._isInvalid || !this._isRelative)
|
| + return;
|
| + parse.call(this, hostname, 'hostname');
|
| + },
|
| +
|
| + get port() {
|
| + return this._port;
|
| + },
|
| + set port(port) {
|
| + if (this._isInvalid || !this._isRelative)
|
| + return;
|
| + parse.call(this, port, 'port');
|
| + },
|
| +
|
| + get pathname() {
|
| + return this._isInvalid ? '' : this._isRelative ?
|
| + '/' + this._path.join('/') : this._schemeData;
|
| + },
|
| + set pathname(pathname) {
|
| + if (this._isInvalid || !this._isRelative)
|
| + return;
|
| + this._path = [];
|
| + parse.call(this, pathname, 'relative path start');
|
| + },
|
| +
|
| + get search() {
|
| + return this._isInvalid || !this._query || '?' == this._query ?
|
| + '' : this._query;
|
| + },
|
| + set search(search) {
|
| + if (this._isInvalid || !this._isRelative)
|
| + return;
|
| + this._query = '?';
|
| + if ('?' == search[0])
|
| + search = search.slice(1);
|
| + parse.call(this, search, 'query');
|
| + },
|
| +
|
| + get hash() {
|
| + return this._isInvalid || !this._fragment || '#' == this._fragment ?
|
| + '' : this._fragment;
|
| + },
|
| + set hash(hash) {
|
| + if (this._isInvalid)
|
| + return;
|
| + this._fragment = '#';
|
| + if ('#' == hash[0])
|
| + hash = hash.slice(1);
|
| + parse.call(this, hash, 'fragment');
|
| + },
|
| +
|
| + get origin() {
|
| + var host;
|
| + if (this._isInvalid || !this._scheme) {
|
| + return '';
|
| + }
|
| + // javascript: Gecko returns String(""), WebKit/Blink String("null")
|
| + // Gecko throws error for "data://"
|
| + // data: Gecko returns "", Blink returns "data://", WebKit returns "null"
|
| + // Gecko returns String("") for file: mailto:
|
| + // WebKit/Blink returns String("SCHEME://") for file: mailto:
|
| + switch (this._scheme) {
|
| + case 'data':
|
| + case 'file':
|
| + case 'javascript':
|
| + case 'mailto':
|
| + return 'null';
|
| + }
|
| + host = this.host;
|
| + if (!host) {
|
| + return '';
|
| + }
|
| + return this._scheme + '://' + host;
|
| + }
|
| + };
|
| +
|
| + // Copy over the static methods
|
| + var OriginalURL = scope.URL;
|
| + if (OriginalURL) {
|
| + jURL.createObjectURL = function(blob) {
|
| + // IE extension allows a second optional options argument.
|
| + // http://msdn.microsoft.com/en-us/library/ie/hh772302(v=vs.85).aspx
|
| + return OriginalURL.createObjectURL.apply(OriginalURL, arguments);
|
| + };
|
| + jURL.revokeObjectURL = function(url) {
|
| + OriginalURL.revokeObjectURL(url);
|
| + };
|
| + }
|
| +
|
| + scope.URL = jURL;
|
| +
|
| +})(this);
|
| +
|
| +(function(scope) {
|
| +
|
| +var iterations = 0;
|
| +var callbacks = [];
|
| +var twiddle = document.createTextNode('');
|
| +
|
| +function endOfMicrotask(callback) {
|
| + twiddle.textContent = iterations++;
|
| + callbacks.push(callback);
|
| +}
|
| +
|
| +function atEndOfMicrotask() {
|
| + while (callbacks.length) {
|
| + callbacks.shift()();
|
| + }
|
| +}
|
| +
|
| +new (window.MutationObserver || JsMutationObserver)(atEndOfMicrotask)
|
| + .observe(twiddle, {characterData: true})
|
| + ;
|
| +
|
| +// exports
|
| +scope.endOfMicrotask = endOfMicrotask;
|
| +// bc
|
| +Platform.endOfMicrotask = endOfMicrotask;
|
| +
|
| +})(Polymer);
|
| +
|
| +
|
| +(function(scope) {
|
| +
|
| +/**
|
| + * @class Polymer
|
| + */
|
| +
|
| +// imports
|
| +var endOfMicrotask = scope.endOfMicrotask;
|
| +
|
| +// logging
|
| +var log = window.WebComponents ? WebComponents.flags.log : {};
|
| +
|
| +// inject style sheet
|
| +var style = document.createElement('style');
|
| +style.textContent = 'template {display: none !important;} /* injected by platform.js */';
|
| +var head = document.querySelector('head');
|
| +head.insertBefore(style, head.firstChild);
|
| +
|
| +
|
| +/**
|
| + * Force any pending data changes to be observed before
|
| + * the next task. Data changes are processed asynchronously but are guaranteed
|
| + * to be processed, for example, before paintin. This method should rarely be
|
| + * needed. It does nothing when Object.observe is available;
|
| + * when Object.observe is not available, Polymer automatically flushes data
|
| + * changes approximately every 1/10 second.
|
| + * Therefore, `flush` should only be used when a data mutation should be
|
| + * observed sooner than this.
|
| + *
|
| + * @method flush
|
| + */
|
| +// flush (with logging)
|
| +var flushing;
|
| +function flush() {
|
| + if (!flushing) {
|
| + flushing = true;
|
| + endOfMicrotask(function() {
|
| + flushing = false;
|
| + log.data && console.group('flush');
|
| + Platform.performMicrotaskCheckpoint();
|
| + log.data && console.groupEnd();
|
| + });
|
| + }
|
| +};
|
| +
|
| +// polling dirty checker
|
| +// flush periodically if platform does not have object observe.
|
| +if (!Observer.hasObjectObserve) {
|
| + var FLUSH_POLL_INTERVAL = 125;
|
| + window.addEventListener('WebComponentsReady', function() {
|
| + flush();
|
| + // watch document visiblity to toggle dirty-checking
|
| + var visibilityHandler = function() {
|
| + // only flush if the page is visibile
|
| + if (document.visibilityState === 'hidden') {
|
| + if (scope.flushPoll) {
|
| + clearInterval(scope.flushPoll);
|
| + }
|
| + } else {
|
| + scope.flushPoll = setInterval(flush, FLUSH_POLL_INTERVAL);
|
| + }
|
| + };
|
| + if (typeof document.visibilityState === 'string') {
|
| + document.addEventListener('visibilitychange', visibilityHandler);
|
| + }
|
| + visibilityHandler();
|
| + });
|
| +} else {
|
| + // make flush a no-op when we have Object.observe
|
| + flush = function() {};
|
| +}
|
| +
|
| +if (window.CustomElements && !CustomElements.useNative) {
|
| + var originalImportNode = Document.prototype.importNode;
|
| + Document.prototype.importNode = function(node, deep) {
|
| + var imported = originalImportNode.call(this, node, deep);
|
| + CustomElements.upgradeAll(imported);
|
| + return imported;
|
| + };
|
| +}
|
| +
|
| +// exports
|
| +scope.flush = flush;
|
| +// bc
|
| +Platform.flush = flush;
|
| +
|
| +})(window.Polymer);
|
| +
|
| +
|
| +(function(scope) {
|
| +
|
| +var urlResolver = {
|
| + resolveDom: function(root, url) {
|
| + url = url || baseUrl(root);
|
| + this.resolveAttributes(root, url);
|
| + this.resolveStyles(root, url);
|
| + // handle template.content
|
| + var templates = root.querySelectorAll('template');
|
| + if (templates) {
|
| + for (var i = 0, l = templates.length, t; (i < l) && (t = templates[i]); i++) {
|
| + if (t.content) {
|
| + this.resolveDom(t.content, url);
|
| + }
|
| + }
|
| + }
|
| + },
|
| + resolveTemplate: function(template) {
|
| + this.resolveDom(template.content, baseUrl(template));
|
| + },
|
| + resolveStyles: function(root, url) {
|
| + var styles = root.querySelectorAll('style');
|
| + if (styles) {
|
| + for (var i = 0, l = styles.length, s; (i < l) && (s = styles[i]); i++) {
|
| + this.resolveStyle(s, url);
|
| + }
|
| + }
|
| + },
|
| + resolveStyle: function(style, url) {
|
| + url = url || baseUrl(style);
|
| + style.textContent = this.resolveCssText(style.textContent, url);
|
| + },
|
| + resolveCssText: function(cssText, baseUrl, keepAbsolute) {
|
| + cssText = replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_URL_REGEXP);
|
| + return replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, CSS_IMPORT_REGEXP);
|
| + },
|
| + resolveAttributes: function(root, url) {
|
| + if (root.hasAttributes && root.hasAttributes()) {
|
| + this.resolveElementAttributes(root, url);
|
| + }
|
| + // search for attributes that host urls
|
| + var nodes = root && root.querySelectorAll(URL_ATTRS_SELECTOR);
|
| + if (nodes) {
|
| + for (var i = 0, l = nodes.length, n; (i < l) && (n = nodes[i]); i++) {
|
| + this.resolveElementAttributes(n, url);
|
| + }
|
| + }
|
| + },
|
| + resolveElementAttributes: function(node, url) {
|
| + url = url || baseUrl(node);
|
| + URL_ATTRS.forEach(function(v) {
|
| + var attr = node.attributes[v];
|
| + var value = attr && attr.value;
|
| + var replacement;
|
| + if (value && value.search(URL_TEMPLATE_SEARCH) < 0) {
|
| + if (v === 'style') {
|
| + replacement = replaceUrlsInCssText(value, url, false, CSS_URL_REGEXP);
|
| + } else {
|
| + replacement = resolveRelativeUrl(url, value);
|
| + }
|
| + attr.value = replacement;
|
| + }
|
| + });
|
| + }
|
| +};
|
| +
|
| +var CSS_URL_REGEXP = /(url\()([^)]*)(\))/g;
|
| +var CSS_IMPORT_REGEXP = /(@import[\s]+(?!url\())([^;]*)(;)/g;
|
| +var URL_ATTRS = ['href', 'src', 'action', 'style', 'url'];
|
| +var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
|
| +var URL_TEMPLATE_SEARCH = '{{.*}}';
|
| +var URL_HASH = '#';
|
| +
|
| +function baseUrl(node) {
|
| + var u = new URL(node.ownerDocument.baseURI);
|
| + u.search = '';
|
| + u.hash = '';
|
| + return u;
|
| +}
|
| +
|
| +function replaceUrlsInCssText(cssText, baseUrl, keepAbsolute, regexp) {
|
| + return cssText.replace(regexp, function(m, pre, url, post) {
|
| + var urlPath = url.replace(/["']/g, '');
|
| + urlPath = resolveRelativeUrl(baseUrl, urlPath, keepAbsolute);
|
| + return pre + '\'' + urlPath + '\'' + post;
|
| + });
|
| +}
|
| +
|
| +function resolveRelativeUrl(baseUrl, url, keepAbsolute) {
|
| + // do not resolve '/' absolute urls
|
| + if (url && url[0] === '/') {
|
| + return url;
|
| + }
|
| + var u = new URL(url, baseUrl);
|
| + return keepAbsolute ? u.href : makeDocumentRelPath(u.href);
|
| +}
|
| +
|
| +function makeDocumentRelPath(url) {
|
| + var root = baseUrl(document.documentElement);
|
| + var u = new URL(url, root);
|
| + if (u.host === root.host && u.port === root.port &&
|
| + u.protocol === root.protocol) {
|
| + return makeRelPath(root, u);
|
| + } else {
|
| + return url;
|
| + }
|
| +}
|
| +
|
| +// make a relative path from source to target
|
| +function makeRelPath(sourceUrl, targetUrl) {
|
| + var source = sourceUrl.pathname;
|
| + var target = targetUrl.pathname;
|
| + var s = source.split('/');
|
| + var t = target.split('/');
|
| + while (s.length && s[0] === t[0]){
|
| + s.shift();
|
| + t.shift();
|
| + }
|
| + for (var i = 0, l = s.length - 1; i < l; i++) {
|
| + t.unshift('..');
|
| + }
|
| + // empty '#' is discarded but we need to preserve it.
|
| + var hash = (targetUrl.href.slice(-1) === URL_HASH) ? URL_HASH : targetUrl.hash;
|
| + return t.join('/') + targetUrl.search + hash;
|
| +}
|
| +
|
| +// exports
|
| +scope.urlResolver = urlResolver;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| + var endOfMicrotask = Polymer.endOfMicrotask;
|
| +
|
| + // Generic url loader
|
| + function Loader(regex) {
|
| + this.cache = Object.create(null);
|
| + this.map = Object.create(null);
|
| + this.requests = 0;
|
| + this.regex = regex;
|
| + }
|
| + Loader.prototype = {
|
| +
|
| + // TODO(dfreedm): there may be a better factoring here
|
| + // extract absolute urls from the text (full of relative urls)
|
| + extractUrls: function(text, base) {
|
| + var matches = [];
|
| + var matched, u;
|
| + while ((matched = this.regex.exec(text))) {
|
| + u = new URL(matched[1], base);
|
| + matches.push({matched: matched[0], url: u.href});
|
| + }
|
| + return matches;
|
| + },
|
| + // take a text blob, a root url, and a callback and load all the urls found within the text
|
| + // returns a map of absolute url to text
|
| + process: function(text, root, callback) {
|
| + var matches = this.extractUrls(text, root);
|
| +
|
| + // every call to process returns all the text this loader has ever received
|
| + var done = callback.bind(null, this.map);
|
| + this.fetch(matches, done);
|
| + },
|
| + // build a mapping of url -> text from matches
|
| + fetch: function(matches, callback) {
|
| + var inflight = matches.length;
|
| +
|
| + // return early if there is no fetching to be done
|
| + if (!inflight) {
|
| + return callback();
|
| + }
|
| +
|
| + // wait for all subrequests to return
|
| + var done = function() {
|
| + if (--inflight === 0) {
|
| + callback();
|
| + }
|
| + };
|
| +
|
| + // start fetching all subrequests
|
| + var m, req, url;
|
| + for (var i = 0; i < inflight; i++) {
|
| + m = matches[i];
|
| + url = m.url;
|
| + req = this.cache[url];
|
| + // if this url has already been requested, skip requesting it again
|
| + if (!req) {
|
| + req = this.xhr(url);
|
| + req.match = m;
|
| + this.cache[url] = req;
|
| + }
|
| + // wait for the request to process its subrequests
|
| + req.wait(done);
|
| + }
|
| + },
|
| + handleXhr: function(request) {
|
| + var match = request.match;
|
| + var url = match.url;
|
| +
|
| + // handle errors with an empty string
|
| + var response = request.response || request.responseText || '';
|
| + this.map[url] = response;
|
| + this.fetch(this.extractUrls(response, url), request.resolve);
|
| + },
|
| + xhr: function(url) {
|
| + this.requests++;
|
| + var request = new XMLHttpRequest();
|
| + request.open('GET', url, true);
|
| + request.send();
|
| + request.onerror = request.onload = this.handleXhr.bind(this, request);
|
| +
|
| + // queue of tasks to run after XHR returns
|
| + request.pending = [];
|
| + request.resolve = function() {
|
| + var pending = request.pending;
|
| + for(var i = 0; i < pending.length; i++) {
|
| + pending[i]();
|
| + }
|
| + request.pending = null;
|
| + };
|
| +
|
| + // if we have already resolved, pending is null, async call the callback
|
| + request.wait = function(fn) {
|
| + if (request.pending) {
|
| + request.pending.push(fn);
|
| + } else {
|
| + endOfMicrotask(fn);
|
| + }
|
| + };
|
| +
|
| + return request;
|
| + }
|
| + };
|
| +
|
| + scope.Loader = Loader;
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| +var urlResolver = scope.urlResolver;
|
| +var Loader = scope.Loader;
|
| +
|
| +function StyleResolver() {
|
| + this.loader = new Loader(this.regex);
|
| +}
|
| +StyleResolver.prototype = {
|
| + regex: /@import\s+(?:url)?["'\(]*([^'"\)]*)['"\)]*;/g,
|
| + // Recursively replace @imports with the text at that url
|
| + resolve: function(text, url, callback) {
|
| + var done = function(map) {
|
| + callback(this.flatten(text, url, map));
|
| + }.bind(this);
|
| + this.loader.process(text, url, done);
|
| + },
|
| + // resolve the textContent of a style node
|
| + resolveNode: function(style, url, callback) {
|
| + var text = style.textContent;
|
| + var done = function(text) {
|
| + style.textContent = text;
|
| + callback(style);
|
| + };
|
| + this.resolve(text, url, done);
|
| + },
|
| + // flatten all the @imports to text
|
| + flatten: function(text, base, map) {
|
| + var matches = this.loader.extractUrls(text, base);
|
| + var match, url, intermediate;
|
| + for (var i = 0; i < matches.length; i++) {
|
| + match = matches[i];
|
| + url = match.url;
|
| + // resolve any css text to be relative to the importer, keep absolute url
|
| + intermediate = urlResolver.resolveCssText(map[url], url, true);
|
| + // flatten intermediate @imports
|
| + intermediate = this.flatten(intermediate, base, map);
|
| + text = text.replace(match.matched, intermediate);
|
| + }
|
| + return text;
|
| + },
|
| + loadStyles: function(styles, base, callback) {
|
| + var loaded=0, l = styles.length;
|
| + // called in the context of the style
|
| + function loadedStyle(style) {
|
| + loaded++;
|
| + if (loaded === l && callback) {
|
| + callback();
|
| + }
|
| + }
|
| + for (var i=0, s; (i<l) && (s=styles[i]); i++) {
|
| + this.resolveNode(s, base, loadedStyle);
|
| + }
|
| + }
|
| +};
|
| +
|
| +var styleResolver = new StyleResolver();
|
| +
|
| +// exports
|
| +scope.styleResolver = styleResolver;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // copy own properties from 'api' to 'prototype, with name hinting for 'super'
|
| + function extend(prototype, api) {
|
| + if (prototype && api) {
|
| + // use only own properties of 'api'
|
| + Object.getOwnPropertyNames(api).forEach(function(n) {
|
| + // acquire property descriptor
|
| + var pd = Object.getOwnPropertyDescriptor(api, n);
|
| + if (pd) {
|
| + // clone property via descriptor
|
| + Object.defineProperty(prototype, n, pd);
|
| + // cache name-of-method for 'super' engine
|
| + if (typeof pd.value == 'function') {
|
| + // hint the 'super' engine
|
| + pd.value.nom = n;
|
| + }
|
| + }
|
| + });
|
| + }
|
| + return prototype;
|
| + }
|
| +
|
| +
|
| + // mixin
|
| +
|
| + // copy all properties from inProps (et al) to inObj
|
| + function mixin(inObj/*, inProps, inMoreProps, ...*/) {
|
| + var obj = inObj || {};
|
| + for (var i = 1; i < arguments.length; i++) {
|
| + var p = arguments[i];
|
| + try {
|
| + for (var n in p) {
|
| + copyProperty(n, p, obj);
|
| + }
|
| + } catch(x) {
|
| + }
|
| + }
|
| + return obj;
|
| + }
|
| +
|
| + // copy property inName from inSource object to inTarget object
|
| + function copyProperty(inName, inSource, inTarget) {
|
| + var pd = getPropertyDescriptor(inSource, inName);
|
| + Object.defineProperty(inTarget, inName, pd);
|
| + }
|
| +
|
| + // get property descriptor for inName on inObject, even if
|
| + // inName exists on some link in inObject's prototype chain
|
| + function getPropertyDescriptor(inObject, inName) {
|
| + if (inObject) {
|
| + var pd = Object.getOwnPropertyDescriptor(inObject, inName);
|
| + return pd || getPropertyDescriptor(Object.getPrototypeOf(inObject), inName);
|
| + }
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.extend = extend;
|
| + scope.mixin = mixin;
|
| +
|
| + // for bc
|
| + Platform.mixin = mixin;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // usage
|
| +
|
| + // invoke cb.call(this) in 100ms, unless the job is re-registered,
|
| + // which resets the timer
|
| + //
|
| + // this.myJob = this.job(this.myJob, cb, 100)
|
| + //
|
| + // returns a job handle which can be used to re-register a job
|
| +
|
| + var Job = function(inContext) {
|
| + this.context = inContext;
|
| + this.boundComplete = this.complete.bind(this)
|
| + };
|
| + Job.prototype = {
|
| + go: function(callback, wait) {
|
| + this.callback = callback;
|
| + var h;
|
| + if (!wait) {
|
| + h = requestAnimationFrame(this.boundComplete);
|
| + this.handle = function() {
|
| + cancelAnimationFrame(h);
|
| + }
|
| + } else {
|
| + h = setTimeout(this.boundComplete, wait);
|
| + this.handle = function() {
|
| + clearTimeout(h);
|
| + }
|
| + }
|
| + },
|
| + stop: function() {
|
| + if (this.handle) {
|
| + this.handle();
|
| + this.handle = null;
|
| + }
|
| + },
|
| + complete: function() {
|
| + if (this.handle) {
|
| + this.stop();
|
| + this.callback.call(this.context);
|
| + }
|
| + }
|
| + };
|
| +
|
| + function job(job, callback, wait) {
|
| + if (job) {
|
| + job.stop();
|
| + } else {
|
| + job = new Job(this);
|
| + }
|
| + job.go(callback, wait);
|
| + return job;
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.job = job;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // dom polyfill, additions, and utility methods
|
| +
|
| + var registry = {};
|
| +
|
| + HTMLElement.register = function(tag, prototype) {
|
| + registry[tag] = prototype;
|
| + };
|
| +
|
| + // get prototype mapped to node <tag>
|
| + HTMLElement.getPrototypeForTag = function(tag) {
|
| + var prototype = !tag ? HTMLElement.prototype : registry[tag];
|
| + // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects
|
| + return prototype || Object.getPrototypeOf(document.createElement(tag));
|
| + };
|
| +
|
| + // we have to flag propagation stoppage for the event dispatcher
|
| + var originalStopPropagation = Event.prototype.stopPropagation;
|
| + Event.prototype.stopPropagation = function() {
|
| + this.cancelBubble = true;
|
| + originalStopPropagation.apply(this, arguments);
|
| + };
|
| +
|
| +
|
| + // polyfill DOMTokenList
|
| + // * add/remove: allow these methods to take multiple classNames
|
| + // * toggle: add a 2nd argument which forces the given state rather
|
| + // than toggling.
|
| +
|
| + var add = DOMTokenList.prototype.add;
|
| + var remove = DOMTokenList.prototype.remove;
|
| + DOMTokenList.prototype.add = function() {
|
| + for (var i = 0; i < arguments.length; i++) {
|
| + add.call(this, arguments[i]);
|
| + }
|
| + };
|
| + DOMTokenList.prototype.remove = function() {
|
| + for (var i = 0; i < arguments.length; i++) {
|
| + remove.call(this, arguments[i]);
|
| + }
|
| + };
|
| + DOMTokenList.prototype.toggle = function(name, bool) {
|
| + if (arguments.length == 1) {
|
| + bool = !this.contains(name);
|
| + }
|
| + bool ? this.add(name) : this.remove(name);
|
| + };
|
| + DOMTokenList.prototype.switch = function(oldName, newName) {
|
| + oldName && this.remove(oldName);
|
| + newName && this.add(newName);
|
| + };
|
| +
|
| + // add array() to NodeList, NamedNodeMap, HTMLCollection
|
| +
|
| + var ArraySlice = function() {
|
| + return Array.prototype.slice.call(this);
|
| + };
|
| +
|
| + var namedNodeMap = (window.NamedNodeMap || window.MozNamedAttrMap || {});
|
| +
|
| + NodeList.prototype.array = ArraySlice;
|
| + namedNodeMap.prototype.array = ArraySlice;
|
| + HTMLCollection.prototype.array = ArraySlice;
|
| +
|
| + // utility
|
| +
|
| + function createDOM(inTagOrNode, inHTML, inAttrs) {
|
| + var dom = typeof inTagOrNode == 'string' ?
|
| + document.createElement(inTagOrNode) : inTagOrNode.cloneNode(true);
|
| + dom.innerHTML = inHTML;
|
| + if (inAttrs) {
|
| + for (var n in inAttrs) {
|
| + dom.setAttribute(n, inAttrs[n]);
|
| + }
|
| + }
|
| + return dom;
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.createDOM = createDOM;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| + // super
|
| +
|
| + // `arrayOfArgs` is an optional array of args like one might pass
|
| + // to `Function.apply`
|
| +
|
| + // TODO(sjmiles):
|
| + // $super must be installed on an instance or prototype chain
|
| + // as `super`, and invoked via `this`, e.g.
|
| + // `this.super();`
|
| +
|
| + // will not work if function objects are not unique, for example,
|
| + // when using mixins.
|
| + // The memoization strategy assumes each function exists on only one
|
| + // prototype chain i.e. we use the function object for memoizing)
|
| + // perhaps we can bookkeep on the prototype itself instead
|
| + function $super(arrayOfArgs) {
|
| + // since we are thunking a method call, performance is important here:
|
| + // memoize all lookups, once memoized the fast path calls no other
|
| + // functions
|
| + //
|
| + // find the caller (cannot be `strict` because of 'caller')
|
| + var caller = $super.caller;
|
| + // memoized 'name of method'
|
| + var nom = caller.nom;
|
| + // memoized next implementation prototype
|
| + var _super = caller._super;
|
| + if (!_super) {
|
| + if (!nom) {
|
| + nom = caller.nom = nameInThis.call(this, caller);
|
| + }
|
| + if (!nom) {
|
| + console.warn('called super() on a method not installed declaratively (has no .nom property)');
|
| + }
|
| + // super prototype is either cached or we have to find it
|
| + // by searching __proto__ (at the 'top')
|
| + // invariant: because we cache _super on fn below, we never reach
|
| + // here from inside a series of calls to super(), so it's ok to
|
| + // start searching from the prototype of 'this' (at the 'top')
|
| + // we must never memoize a null super for this reason
|
| + _super = memoizeSuper(caller, nom, getPrototypeOf(this));
|
| + }
|
| + // our super function
|
| + var fn = _super[nom];
|
| + if (fn) {
|
| + // memoize information so 'fn' can call 'super'
|
| + if (!fn._super) {
|
| + // must not memoize null, or we lose our invariant above
|
| + memoizeSuper(fn, nom, _super);
|
| + }
|
| + // invoke the inherited method
|
| + // if 'fn' is not function valued, this will throw
|
| + return fn.apply(this, arrayOfArgs || []);
|
| + }
|
| + }
|
| +
|
| + function nameInThis(value) {
|
| + var p = this.__proto__;
|
| + while (p && p !== HTMLElement.prototype) {
|
| + // TODO(sjmiles): getOwnPropertyNames is absurdly expensive
|
| + var n$ = Object.getOwnPropertyNames(p);
|
| + for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) {
|
| + var d = Object.getOwnPropertyDescriptor(p, n);
|
| + if (typeof d.value === 'function' && d.value === value) {
|
| + return n;
|
| + }
|
| + }
|
| + p = p.__proto__;
|
| + }
|
| + }
|
| +
|
| + function memoizeSuper(method, name, proto) {
|
| + // find and cache next prototype containing `name`
|
| + // we need the prototype so we can do another lookup
|
| + // from here
|
| + var s = nextSuper(proto, name, method);
|
| + if (s[name]) {
|
| + // `s` is a prototype, the actual method is `s[name]`
|
| + // tag super method with it's name for quicker lookups
|
| + s[name].nom = name;
|
| + }
|
| + return method._super = s;
|
| + }
|
| +
|
| + function nextSuper(proto, name, caller) {
|
| + // look for an inherited prototype that implements name
|
| + while (proto) {
|
| + if ((proto[name] !== caller) && proto[name]) {
|
| + return proto;
|
| + }
|
| + proto = getPrototypeOf(proto);
|
| + }
|
| + // must not return null, or we lose our invariant above
|
| + // in this case, a super() call was invoked where no superclass
|
| + // method exists
|
| + // TODO(sjmiles): thow an exception?
|
| + return Object;
|
| + }
|
| +
|
| + // NOTE: In some platforms (IE10) the prototype chain is faked via
|
| + // __proto__. Therefore, always get prototype via __proto__ instead of
|
| + // the more standard Object.getPrototypeOf.
|
| + function getPrototypeOf(prototype) {
|
| + return prototype.__proto__;
|
| + }
|
| +
|
| + // utility function to precompute name tags for functions
|
| + // in a (unchained) prototype
|
| + function hintSuper(prototype) {
|
| + // tag functions with their prototype name to optimize
|
| + // super call invocations
|
| + for (var n in prototype) {
|
| + var pd = Object.getOwnPropertyDescriptor(prototype, n);
|
| + if (pd && typeof pd.value === 'function') {
|
| + pd.value.nom = n;
|
| + }
|
| + }
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.super = $super;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + function noopHandler(value) {
|
| + return value;
|
| + }
|
| +
|
| + // helper for deserializing properties of various types to strings
|
| + var typeHandlers = {
|
| + string: noopHandler,
|
| + 'undefined': noopHandler,
|
| + date: function(value) {
|
| + return new Date(Date.parse(value) || Date.now());
|
| + },
|
| + boolean: function(value) {
|
| + if (value === '') {
|
| + return true;
|
| + }
|
| + return value === 'false' ? false : !!value;
|
| + },
|
| + number: function(value) {
|
| + var n = parseFloat(value);
|
| + // hex values like "0xFFFF" parseFloat as 0
|
| + if (n === 0) {
|
| + n = parseInt(value);
|
| + }
|
| + return isNaN(n) ? value : n;
|
| + // this code disabled because encoded values (like "0xFFFF")
|
| + // do not round trip to their original format
|
| + //return (String(floatVal) === value) ? floatVal : value;
|
| + },
|
| + object: function(value, currentValue) {
|
| + if (currentValue === null) {
|
| + return value;
|
| + }
|
| + try {
|
| + // If the string is an object, we can parse is with the JSON library.
|
| + // include convenience replace for single-quotes. If the author omits
|
| + // quotes altogether, parse will fail.
|
| + return JSON.parse(value.replace(/'/g, '"'));
|
| + } catch(e) {
|
| + // The object isn't valid JSON, return the raw value
|
| + return value;
|
| + }
|
| + },
|
| + // avoid deserialization of functions
|
| + 'function': function(value, currentValue) {
|
| + return currentValue;
|
| + }
|
| + };
|
| +
|
| + function deserializeValue(value, currentValue) {
|
| + // attempt to infer type from default value
|
| + var inferredType = typeof currentValue;
|
| + // invent 'date' type value for Date
|
| + if (currentValue instanceof Date) {
|
| + inferredType = 'date';
|
| + }
|
| + // delegate deserialization via type string
|
| + return typeHandlers[inferredType](value, currentValue);
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.deserializeValue = deserializeValue;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var extend = scope.extend;
|
| +
|
| + // module
|
| +
|
| + var api = {};
|
| +
|
| + api.declaration = {};
|
| + api.instance = {};
|
| +
|
| + api.publish = function(apis, prototype) {
|
| + for (var n in apis) {
|
| + extend(prototype, apis[n]);
|
| + }
|
| + };
|
| +
|
| + // exports
|
| +
|
| + scope.api = api;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + /**
|
| + * @class polymer-base
|
| + */
|
| +
|
| + var utils = {
|
| +
|
| + /**
|
| + * Invokes a function asynchronously. The context of the callback
|
| + * function is bound to 'this' automatically. Returns a handle which may
|
| + * be passed to <a href="#cancelAsync">cancelAsync</a> to cancel the
|
| + * asynchronous call.
|
| + *
|
| + * @method async
|
| + * @param {Function|String} method
|
| + * @param {any|Array} args
|
| + * @param {number} timeout
|
| + */
|
| + async: function(method, args, timeout) {
|
| + // when polyfilling Object.observe, ensure changes
|
| + // propagate before executing the async method
|
| + Polymer.flush();
|
| + // second argument to `apply` must be an array
|
| + args = (args && args.length) ? args : [args];
|
| + // function to invoke
|
| + var fn = function() {
|
| + (this[method] || method).apply(this, args);
|
| + }.bind(this);
|
| + // execute `fn` sooner or later
|
| + var handle = timeout ? setTimeout(fn, timeout) :
|
| + requestAnimationFrame(fn);
|
| + // NOTE: switch on inverting handle to determine which time is used.
|
| + return timeout ? handle : ~handle;
|
| + },
|
| +
|
| + /**
|
| + * Cancels a pending callback that was scheduled via
|
| + * <a href="#async">async</a>.
|
| + *
|
| + * @method cancelAsync
|
| + * @param {handle} handle Handle of the `async` to cancel.
|
| + */
|
| + cancelAsync: function(handle) {
|
| + if (handle < 0) {
|
| + cancelAnimationFrame(~handle);
|
| + } else {
|
| + clearTimeout(handle);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Fire an event.
|
| + *
|
| + * @method fire
|
| + * @returns {Object} event
|
| + * @param {string} type An event name.
|
| + * @param {any} detail
|
| + * @param {Node} onNode Target node.
|
| + * @param {Boolean} bubbles Set false to prevent bubbling, defaults to true
|
| + * @param {Boolean} cancelable Set false to prevent cancellation, defaults to true
|
| + */
|
| + fire: function(type, detail, onNode, bubbles, cancelable) {
|
| + var node = onNode || this;
|
| + var detail = detail === null || detail === undefined ? {} : detail;
|
| + var event = new CustomEvent(type, {
|
| + bubbles: bubbles !== undefined ? bubbles : true,
|
| + cancelable: cancelable !== undefined ? cancelable : true,
|
| + detail: detail
|
| + });
|
| + node.dispatchEvent(event);
|
| + return event;
|
| + },
|
| +
|
| + /**
|
| + * Fire an event asynchronously.
|
| + *
|
| + * @method asyncFire
|
| + * @param {string} type An event name.
|
| + * @param detail
|
| + * @param {Node} toNode Target node.
|
| + */
|
| + asyncFire: function(/*inType, inDetail*/) {
|
| + this.async("fire", arguments);
|
| + },
|
| +
|
| + /**
|
| + * Remove class from old, add class to anew, if they exist.
|
| + *
|
| + * @param classFollows
|
| + * @param anew A node.
|
| + * @param old A node
|
| + * @param className
|
| + */
|
| + classFollows: function(anew, old, className) {
|
| + if (old) {
|
| + old.classList.remove(className);
|
| + }
|
| + if (anew) {
|
| + anew.classList.add(className);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Inject HTML which contains markup bound to this element into
|
| + * a target element (replacing target element content).
|
| + *
|
| + * @param String html to inject
|
| + * @param Element target element
|
| + */
|
| + injectBoundHTML: function(html, element) {
|
| + var template = document.createElement('template');
|
| + template.innerHTML = html;
|
| + var fragment = this.instanceTemplate(template);
|
| + if (element) {
|
| + element.textContent = '';
|
| + element.appendChild(fragment);
|
| + }
|
| + return fragment;
|
| + }
|
| + };
|
| +
|
| + // no-operation function for handy stubs
|
| + var nop = function() {};
|
| +
|
| + // null-object for handy stubs
|
| + var nob = {};
|
| +
|
| + // deprecated
|
| +
|
| + utils.asyncMethod = utils.async;
|
| +
|
| + // exports
|
| +
|
| + scope.api.instance.utils = utils;
|
| + scope.nop = nop;
|
| + scope.nob = nob;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| + var EVENT_PREFIX = 'on-';
|
| +
|
| + // instance events api
|
| + var events = {
|
| + // read-only
|
| + EVENT_PREFIX: EVENT_PREFIX,
|
| + // event listeners on host
|
| + addHostListeners: function() {
|
| + var events = this.eventDelegates;
|
| + log.events && (Object.keys(events).length > 0) && console.log('[%s] addHostListeners:', this.localName, events);
|
| + // NOTE: host events look like bindings but really are not;
|
| + // (1) we don't want the attribute to be set and (2) we want to support
|
| + // multiple event listeners ('host' and 'instance') and Node.bind
|
| + // by default supports 1 thing being bound.
|
| + for (var type in events) {
|
| + var methodName = events[type];
|
| + PolymerGestures.addEventListener(this, type, this.element.getEventHandler(this, this, methodName));
|
| + }
|
| + },
|
| + // call 'method' or function method on 'obj' with 'args', if the method exists
|
| + dispatchMethod: function(obj, method, args) {
|
| + if (obj) {
|
| + log.events && console.group('[%s] dispatch [%s]', obj.localName, method);
|
| + var fn = typeof method === 'function' ? method : obj[method];
|
| + if (fn) {
|
| + fn[args ? 'apply' : 'call'](obj, args);
|
| + }
|
| + log.events && console.groupEnd();
|
| + // NOTE: dirty check right after calling method to ensure
|
| + // changes apply quickly; in a very complicated app using high
|
| + // frequency events, this can be a perf concern; in this case,
|
| + // imperative handlers can be used to avoid flushing.
|
| + Polymer.flush();
|
| + }
|
| + }
|
| + };
|
| +
|
| + // exports
|
| +
|
| + scope.api.instance.events = events;
|
| +
|
| + /**
|
| + * @class Polymer
|
| + */
|
| +
|
| + /**
|
| + * Add a gesture aware event handler to the given `node`. Can be used
|
| + * in place of `element.addEventListener` and ensures gestures will function
|
| + * as expected on mobile platforms. Please note that Polymer's declarative
|
| + * event handlers include this functionality by default.
|
| + *
|
| + * @method addEventListener
|
| + * @param {Node} node node on which to listen
|
| + * @param {String} eventType name of the event
|
| + * @param {Function} handlerFn event handler function
|
| + * @param {Boolean} capture set to true to invoke event capturing
|
| + * @type Function
|
| + */
|
| + // alias PolymerGestures event listener logic
|
| + scope.addEventListener = function(node, eventType, handlerFn, capture) {
|
| + PolymerGestures.addEventListener(wrap(node), eventType, handlerFn, capture);
|
| + };
|
| +
|
| + /**
|
| + * Remove a gesture aware event handler on the given `node`. To remove an
|
| + * event listener, the exact same arguments are required that were passed
|
| + * to `Polymer.addEventListener`.
|
| + *
|
| + * @method removeEventListener
|
| + * @param {Node} node node on which to listen
|
| + * @param {String} eventType name of the event
|
| + * @param {Function} handlerFn event handler function
|
| + * @param {Boolean} capture set to true to invoke event capturing
|
| + * @type Function
|
| + */
|
| + scope.removeEventListener = function(node, eventType, handlerFn, capture) {
|
| + PolymerGestures.removeEventListener(wrap(node), eventType, handlerFn, capture);
|
| + };
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // instance api for attributes
|
| +
|
| + var attributes = {
|
| + // copy attributes defined in the element declaration to the instance
|
| + // e.g. <polymer-element name="x-foo" tabIndex="0"> tabIndex is copied
|
| + // to the element instance here.
|
| + copyInstanceAttributes: function () {
|
| + var a$ = this._instanceAttributes;
|
| + for (var k in a$) {
|
| + if (!this.hasAttribute(k)) {
|
| + this.setAttribute(k, a$[k]);
|
| + }
|
| + }
|
| + },
|
| + // for each attribute on this, deserialize value to property as needed
|
| + takeAttributes: function() {
|
| + // if we have no publish lookup table, we have no attributes to take
|
| + // TODO(sjmiles): ad hoc
|
| + if (this._publishLC) {
|
| + for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
|
| + this.attributeToProperty(a.name, a.value);
|
| + }
|
| + }
|
| + },
|
| + // if attribute 'name' is mapped to a property, deserialize
|
| + // 'value' into that property
|
| + attributeToProperty: function(name, value) {
|
| + // try to match this attribute to a property (attributes are
|
| + // all lower-case, so this is case-insensitive search)
|
| + var name = this.propertyForAttribute(name);
|
| + if (name) {
|
| + // filter out 'mustached' values, these are to be
|
| + // replaced with bound-data and are not yet values
|
| + // themselves
|
| + if (value && value.search(scope.bindPattern) >= 0) {
|
| + return;
|
| + }
|
| + // get original value
|
| + var currentValue = this[name];
|
| + // deserialize Boolean or Number values from attribute
|
| + var value = this.deserializeValue(value, currentValue);
|
| + // only act if the value has changed
|
| + if (value !== currentValue) {
|
| + // install new value (has side-effects)
|
| + this[name] = value;
|
| + }
|
| + }
|
| + },
|
| + // return the published property matching name, or undefined
|
| + propertyForAttribute: function(name) {
|
| + var match = this._publishLC && this._publishLC[name];
|
| + return match;
|
| + },
|
| + // convert representation of `stringValue` based on type of `currentValue`
|
| + deserializeValue: function(stringValue, currentValue) {
|
| + return scope.deserializeValue(stringValue, currentValue);
|
| + },
|
| + // convert to a string value based on the type of `inferredType`
|
| + serializeValue: function(value, inferredType) {
|
| + if (inferredType === 'boolean') {
|
| + return value ? '' : undefined;
|
| + } else if (inferredType !== 'object' && inferredType !== 'function'
|
| + && value !== undefined) {
|
| + return value;
|
| + }
|
| + },
|
| + // serializes `name` property value and updates the corresponding attribute
|
| + // note that reflection is opt-in.
|
| + reflectPropertyToAttribute: function(name) {
|
| + var inferredType = typeof this[name];
|
| + // try to intelligently serialize property value
|
| + var serializedValue = this.serializeValue(this[name], inferredType);
|
| + // boolean properties must reflect as boolean attributes
|
| + if (serializedValue !== undefined) {
|
| + this.setAttribute(name, serializedValue);
|
| + // TODO(sorvell): we should remove attr for all properties
|
| + // that have undefined serialization; however, we will need to
|
| + // refine the attr reflection system to achieve this; pica, for example,
|
| + // relies on having inferredType object properties not removed as
|
| + // attrs.
|
| + } else if (inferredType === 'boolean') {
|
| + this.removeAttribute(name);
|
| + }
|
| + }
|
| + };
|
| +
|
| + // exports
|
| +
|
| + scope.api.instance.attributes = attributes;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + /**
|
| + * @class polymer-base
|
| + */
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| +
|
| + // magic words
|
| +
|
| + var OBSERVE_SUFFIX = 'Changed';
|
| +
|
| + // element api
|
| +
|
| + var empty = [];
|
| +
|
| + var updateRecord = {
|
| + object: undefined,
|
| + type: 'update',
|
| + name: undefined,
|
| + oldValue: undefined
|
| + };
|
| +
|
| + var numberIsNaN = Number.isNaN || function(value) {
|
| + return typeof value === 'number' && isNaN(value);
|
| + };
|
| +
|
| + function areSameValue(left, right) {
|
| + if (left === right)
|
| + return left !== 0 || 1 / left === 1 / right;
|
| + if (numberIsNaN(left) && numberIsNaN(right))
|
| + return true;
|
| + return left !== left && right !== right;
|
| + }
|
| +
|
| + // capture A's value if B's value is null or undefined,
|
| + // otherwise use B's value
|
| + function resolveBindingValue(oldValue, value) {
|
| + if (value === undefined && oldValue === null) {
|
| + return value;
|
| + }
|
| + return (value === null || value === undefined) ? oldValue : value;
|
| + }
|
| +
|
| + var properties = {
|
| +
|
| + // creates a CompoundObserver to observe property changes
|
| + // NOTE, this is only done there are any properties in the `observe` object
|
| + createPropertyObserver: function() {
|
| + var n$ = this._observeNames;
|
| + if (n$ && n$.length) {
|
| + var o = this._propertyObserver = new CompoundObserver(true);
|
| + this.registerObserver(o);
|
| + // TODO(sorvell): may not be kosher to access the value here (this[n]);
|
| + // previously we looked at the descriptor on the prototype
|
| + // this doesn't work for inheritance and not for accessors without
|
| + // a value property
|
| + for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
|
| + o.addPath(this, n);
|
| + this.observeArrayValue(n, this[n], null);
|
| + }
|
| + }
|
| + },
|
| +
|
| + // start observing property changes
|
| + openPropertyObserver: function() {
|
| + if (this._propertyObserver) {
|
| + this._propertyObserver.open(this.notifyPropertyChanges, this);
|
| + }
|
| + },
|
| +
|
| + // handler for property changes; routes changes to observing methods
|
| + // note: array valued properties are observed for array splices
|
| + notifyPropertyChanges: function(newValues, oldValues, paths) {
|
| + var name, method, called = {};
|
| + for (var i in oldValues) {
|
| + // note: paths is of form [object, path, object, path]
|
| + name = paths[2 * i + 1];
|
| + method = this.observe[name];
|
| + if (method) {
|
| + var ov = oldValues[i], nv = newValues[i];
|
| + // observes the value if it is an array
|
| + this.observeArrayValue(name, nv, ov);
|
| + if (!called[method]) {
|
| + // only invoke change method if one of ov or nv is not (undefined | null)
|
| + if ((ov !== undefined && ov !== null) || (nv !== undefined && nv !== null)) {
|
| + called[method] = true;
|
| + // TODO(sorvell): call method with the set of values it's expecting;
|
| + // e.g. 'foo bar': 'invalidate' expects the new and old values for
|
| + // foo and bar. Currently we give only one of these and then
|
| + // deliver all the arguments.
|
| + this.invokeMethod(method, [ov, nv, arguments]);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + },
|
| +
|
| + // call method iff it exists.
|
| + invokeMethod: function(method, args) {
|
| + var fn = this[method] || method;
|
| + if (typeof fn === 'function') {
|
| + fn.apply(this, args);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Force any pending property changes to synchronously deliver to
|
| + * handlers specified in the `observe` object.
|
| + * Note, normally changes are processed at microtask time.
|
| + *
|
| + * @method deliverChanges
|
| + */
|
| + deliverChanges: function() {
|
| + if (this._propertyObserver) {
|
| + this._propertyObserver.deliver();
|
| + }
|
| + },
|
| +
|
| + observeArrayValue: function(name, value, old) {
|
| + // we only care if there are registered side-effects
|
| + var callbackName = this.observe[name];
|
| + if (callbackName) {
|
| + // if we are observing the previous value, stop
|
| + if (Array.isArray(old)) {
|
| + log.observe && console.log('[%s] observeArrayValue: unregister observer [%s]', this.localName, name);
|
| + this.closeNamedObserver(name + '__array');
|
| + }
|
| + // if the new value is an array, being observing it
|
| + if (Array.isArray(value)) {
|
| + log.observe && console.log('[%s] observeArrayValue: register observer [%s]', this.localName, name, value);
|
| + var observer = new ArrayObserver(value);
|
| + observer.open(function(splices) {
|
| + this.invokeMethod(callbackName, [splices]);
|
| + }, this);
|
| + this.registerNamedObserver(name + '__array', observer);
|
| + }
|
| + }
|
| + },
|
| +
|
| + emitPropertyChangeRecord: function(name, value, oldValue) {
|
| + var object = this;
|
| + if (areSameValue(value, oldValue)) {
|
| + return;
|
| + }
|
| + // invoke property change side effects
|
| + this._propertyChanged(name, value, oldValue);
|
| + // emit change record
|
| + if (!Observer.hasObjectObserve) {
|
| + return;
|
| + }
|
| + var notifier = this._objectNotifier;
|
| + if (!notifier) {
|
| + notifier = this._objectNotifier = Object.getNotifier(this);
|
| + }
|
| + updateRecord.object = this;
|
| + updateRecord.name = name;
|
| + updateRecord.oldValue = oldValue;
|
| + notifier.notify(updateRecord);
|
| + },
|
| +
|
| + _propertyChanged: function(name, value, oldValue) {
|
| + if (this.reflect[name]) {
|
| + this.reflectPropertyToAttribute(name);
|
| + }
|
| + },
|
| +
|
| + // creates a property binding (called via bind) to a published property.
|
| + bindProperty: function(property, observable, oneTime) {
|
| + if (oneTime) {
|
| + this[property] = observable;
|
| + return;
|
| + }
|
| + var computed = this.element.prototype.computed;
|
| + // Binding an "out-only" value to a computed property. Note that
|
| + // since this observer isn't opened, it doesn't need to be closed on
|
| + // cleanup.
|
| + if (computed && computed[property]) {
|
| + var privateComputedBoundValue = property + 'ComputedBoundObservable_';
|
| + this[privateComputedBoundValue] = observable;
|
| + return;
|
| + }
|
| + return this.bindToAccessor(property, observable, resolveBindingValue);
|
| + },
|
| +
|
| + // NOTE property `name` must be published. This makes it an accessor.
|
| + bindToAccessor: function(name, observable, resolveFn) {
|
| + var privateName = name + '_';
|
| + var privateObservable = name + 'Observable_';
|
| + // Present for properties which are computed and published and have a
|
| + // bound value.
|
| + var privateComputedBoundValue = name + 'ComputedBoundObservable_';
|
| + this[privateObservable] = observable;
|
| + var oldValue = this[privateName];
|
| + // observable callback
|
| + var self = this;
|
| + function updateValue(value, oldValue) {
|
| + self[privateName] = value;
|
| + var setObserveable = self[privateComputedBoundValue];
|
| + if (setObserveable && typeof setObserveable.setValue == 'function') {
|
| + setObserveable.setValue(value);
|
| + }
|
| + self.emitPropertyChangeRecord(name, value, oldValue);
|
| + }
|
| + // resolve initial value
|
| + var value = observable.open(updateValue);
|
| + if (resolveFn && !areSameValue(oldValue, value)) {
|
| + var resolvedValue = resolveFn(oldValue, value);
|
| + if (!areSameValue(value, resolvedValue)) {
|
| + value = resolvedValue;
|
| + if (observable.setValue) {
|
| + observable.setValue(value);
|
| + }
|
| + }
|
| + }
|
| + updateValue(value, oldValue);
|
| + // register and return observable
|
| + var observer = {
|
| + close: function() {
|
| + observable.close();
|
| + self[privateObservable] = undefined;
|
| + self[privateComputedBoundValue] = undefined;
|
| + }
|
| + };
|
| + this.registerObserver(observer);
|
| + return observer;
|
| + },
|
| +
|
| + createComputedProperties: function() {
|
| + if (!this._computedNames) {
|
| + return;
|
| + }
|
| + for (var i = 0; i < this._computedNames.length; i++) {
|
| + var name = this._computedNames[i];
|
| + var expressionText = this.computed[name];
|
| + try {
|
| + var expression = PolymerExpressions.getExpression(expressionText);
|
| + var observable = expression.getBinding(this, this.element.syntax);
|
| + this.bindToAccessor(name, observable);
|
| + } catch (ex) {
|
| + console.error('Failed to create computed property', ex);
|
| + }
|
| + }
|
| + },
|
| +
|
| + // property bookkeeping
|
| + registerObserver: function(observer) {
|
| + if (!this._observers) {
|
| + this._observers = [observer];
|
| + return;
|
| + }
|
| + this._observers.push(observer);
|
| + },
|
| +
|
| + closeObservers: function() {
|
| + if (!this._observers) {
|
| + return;
|
| + }
|
| + // observer array items are arrays of observers.
|
| + var observers = this._observers;
|
| + for (var i = 0; i < observers.length; i++) {
|
| + var observer = observers[i];
|
| + if (observer && typeof observer.close == 'function') {
|
| + observer.close();
|
| + }
|
| + }
|
| + this._observers = [];
|
| + },
|
| +
|
| + // bookkeeping observers for memory management
|
| + registerNamedObserver: function(name, observer) {
|
| + var o$ = this._namedObservers || (this._namedObservers = {});
|
| + o$[name] = observer;
|
| + },
|
| +
|
| + closeNamedObserver: function(name) {
|
| + var o$ = this._namedObservers;
|
| + if (o$ && o$[name]) {
|
| + o$[name].close();
|
| + o$[name] = null;
|
| + return true;
|
| + }
|
| + },
|
| +
|
| + closeNamedObservers: function() {
|
| + if (this._namedObservers) {
|
| + for (var i in this._namedObservers) {
|
| + this.closeNamedObserver(i);
|
| + }
|
| + this._namedObservers = {};
|
| + }
|
| + }
|
| +
|
| + };
|
| +
|
| + // logging
|
| + var LOG_OBSERVE = '[%s] watching [%s]';
|
| + var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]';
|
| + var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]';
|
| +
|
| + // exports
|
| +
|
| + scope.api.instance.properties = properties;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + /**
|
| + * @class polymer-base
|
| + */
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| +
|
| + // element api supporting mdv
|
| + var mdv = {
|
| +
|
| + /**
|
| + * Creates dom cloned from the given template, instantiating bindings
|
| + * with this element as the template model and `PolymerExpressions` as the
|
| + * binding delegate.
|
| + *
|
| + * @method instanceTemplate
|
| + * @param {Template} template source template from which to create dom.
|
| + */
|
| + instanceTemplate: function(template) {
|
| + // ensure template is decorated (lets' things like <tr template ...> work)
|
| + HTMLTemplateElement.decorate(template);
|
| + // ensure a default bindingDelegate
|
| + var syntax = this.syntax || (!template.bindingDelegate &&
|
| + this.element.syntax);
|
| + var dom = template.createInstance(this, syntax);
|
| + var observers = dom.bindings_;
|
| + for (var i = 0; i < observers.length; i++) {
|
| + this.registerObserver(observers[i]);
|
| + }
|
| + return dom;
|
| + },
|
| +
|
| + // Called by TemplateBinding/NodeBind to setup a binding to the given
|
| + // property. It's overridden here to support property bindings
|
| + // in addition to attribute bindings that are supported by default.
|
| + bind: function(name, observable, oneTime) {
|
| + var property = this.propertyForAttribute(name);
|
| + if (!property) {
|
| + // TODO(sjmiles): this mixin method must use the special form
|
| + // of `super` installed by `mixinMethod` in declaration/prototype.js
|
| + return this.mixinSuper(arguments);
|
| + } else {
|
| + // use n-way Polymer binding
|
| + var observer = this.bindProperty(property, observable, oneTime);
|
| + // NOTE: reflecting binding information is typically required only for
|
| + // tooling. It has a performance cost so it's opt-in in Node.bind.
|
| + if (Platform.enableBindingsReflection && observer) {
|
| + observer.path = observable.path_;
|
| + this._recordBinding(property, observer);
|
| + }
|
| + if (this.reflect[property]) {
|
| + this.reflectPropertyToAttribute(property);
|
| + }
|
| + return observer;
|
| + }
|
| + },
|
| +
|
| + _recordBinding: function(name, observer) {
|
| + this.bindings_ = this.bindings_ || {};
|
| + this.bindings_[name] = observer;
|
| + },
|
| +
|
| + // Called by TemplateBinding when all bindings on an element have been
|
| + // executed. This signals that all element inputs have been gathered
|
| + // and it's safe to ready the element, create shadow-root and start
|
| + // data-observation.
|
| + bindFinished: function() {
|
| + this.makeElementReady();
|
| + },
|
| +
|
| + // called at detached time to signal that an element's bindings should be
|
| + // cleaned up. This is done asynchronously so that users have the chance
|
| + // to call `cancelUnbindAll` to prevent unbinding.
|
| + asyncUnbindAll: function() {
|
| + if (!this._unbound) {
|
| + log.unbind && console.log('[%s] asyncUnbindAll', this.localName);
|
| + this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * This method should rarely be used and only if
|
| + * <a href="#cancelUnbindAll">`cancelUnbindAll`</a> has been called to
|
| + * prevent element unbinding. In this case, the element's bindings will
|
| + * not be automatically cleaned up and it cannot be garbage collected
|
| + * by the system. If memory pressure is a concern or a
|
| + * large amount of elements need to be managed in this way, `unbindAll`
|
| + * can be called to deactivate the element's bindings and allow its
|
| + * memory to be reclaimed.
|
| + *
|
| + * @method unbindAll
|
| + */
|
| + unbindAll: function() {
|
| + if (!this._unbound) {
|
| + this.closeObservers();
|
| + this.closeNamedObservers();
|
| + this._unbound = true;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Call in `detached` to prevent the element from unbinding when it is
|
| + * detached from the dom. The element is unbound as a cleanup step that
|
| + * allows its memory to be reclaimed.
|
| + * If `cancelUnbindAll` is used, consider calling
|
| + * <a href="#unbindAll">`unbindAll`</a> when the element is no longer
|
| + * needed. This will allow its memory to be reclaimed.
|
| + *
|
| + * @method cancelUnbindAll
|
| + */
|
| + cancelUnbindAll: function() {
|
| + if (this._unbound) {
|
| + log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAll', this.localName);
|
| + return;
|
| + }
|
| + log.unbind && console.log('[%s] cancelUnbindAll', this.localName);
|
| + if (this._unbindAllJob) {
|
| + this._unbindAllJob = this._unbindAllJob.stop();
|
| + }
|
| + }
|
| +
|
| + };
|
| +
|
| + function unbindNodeTree(node) {
|
| + forNodeTree(node, _nodeUnbindAll);
|
| + }
|
| +
|
| + function _nodeUnbindAll(node) {
|
| + node.unbindAll();
|
| + }
|
| +
|
| + function forNodeTree(node, callback) {
|
| + if (node) {
|
| + callback(node);
|
| + for (var child = node.firstChild; child; child = child.nextSibling) {
|
| + forNodeTree(child, callback);
|
| + }
|
| + }
|
| + }
|
| +
|
| + var mustachePattern = /\{\{([^{}]*)}}/;
|
| +
|
| + // exports
|
| +
|
| + scope.bindPattern = mustachePattern;
|
| + scope.api.instance.mdv = mdv;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + /**
|
| + * Common prototype for all Polymer Elements.
|
| + *
|
| + * @class polymer-base
|
| + * @homepage polymer.github.io
|
| + */
|
| + var base = {
|
| + /**
|
| + * Tags this object as the canonical Base prototype.
|
| + *
|
| + * @property PolymerBase
|
| + * @type boolean
|
| + * @default true
|
| + */
|
| + PolymerBase: true,
|
| +
|
| + /**
|
| + * Debounce signals.
|
| + *
|
| + * Call `job` to defer a named signal, and all subsequent matching signals,
|
| + * until a wait time has elapsed with no new signal.
|
| + *
|
| + * debouncedClickAction: function(e) {
|
| + * // processClick only when it's been 100ms since the last click
|
| + * this.job('click', function() {
|
| + * this.processClick;
|
| + * }, 100);
|
| + * }
|
| + *
|
| + * @method job
|
| + * @param String {String} job A string identifier for the job to debounce.
|
| + * @param Function {Function} callback A function that is called (with `this` context) when the wait time elapses.
|
| + * @param Number {Number} wait Time in milliseconds (ms) after the last signal that must elapse before invoking `callback`
|
| + * @type Handle
|
| + */
|
| + job: function(job, callback, wait) {
|
| + if (typeof job === 'string') {
|
| + var n = '___' + job;
|
| + this[n] = Polymer.job.call(this, this[n], callback, wait);
|
| + } else {
|
| + // TODO(sjmiles): suggest we deprecate this call signature
|
| + return Polymer.job.call(this, job, callback, wait);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Invoke a superclass method.
|
| + *
|
| + * Use `super()` to invoke the most recently overridden call to the
|
| + * currently executing function.
|
| + *
|
| + * To pass arguments through, use the literal `arguments` as the parameter
|
| + * to `super()`.
|
| + *
|
| + * nextPageAction: function(e) {
|
| + * // invoke the superclass version of `nextPageAction`
|
| + * this.super(arguments);
|
| + * }
|
| + *
|
| + * To pass custom arguments, arrange them in an array.
|
| + *
|
| + * appendSerialNo: function(value, serial) {
|
| + * // prefix the superclass serial number with our lot # before
|
| + * // invoking the superlcass
|
| + * return this.super([value, this.lotNo + serial])
|
| + * }
|
| + *
|
| + * @method super
|
| + * @type Any
|
| + * @param {args) An array of arguments to use when calling the superclass method, or null.
|
| + */
|
| + super: Polymer.super,
|
| +
|
| + /**
|
| + * Lifecycle method called when the element is instantiated.
|
| + *
|
| + * Override `created` to perform custom create-time tasks. No need to call
|
| + * super-class `created` unless you are extending another Polymer element.
|
| + * Created is called before the element creates `shadowRoot` or prepares
|
| + * data-observation.
|
| + *
|
| + * @method created
|
| + * @type void
|
| + */
|
| + created: function() {
|
| + },
|
| +
|
| + /**
|
| + * Lifecycle method called when the element has populated it's `shadowRoot`,
|
| + * prepared data-observation, and made itself ready for API interaction.
|
| + *
|
| + * @method ready
|
| + * @type void
|
| + */
|
| + ready: function() {
|
| + },
|
| +
|
| + /**
|
| + * Low-level lifecycle method called as part of standard Custom Elements
|
| + * operation. Polymer implements this method to provide basic default
|
| + * functionality. For custom create-time tasks, implement `created`
|
| + * instead, which is called immediately after `createdCallback`.
|
| + *
|
| + * @method createdCallback
|
| + */
|
| + createdCallback: function() {
|
| + if (this.templateInstance && this.templateInstance.model) {
|
| + console.warn('Attributes on ' + this.localName + ' were data bound ' +
|
| + 'prior to Polymer upgrading the element. This may result in ' +
|
| + 'incorrect binding types.');
|
| + }
|
| + this.created();
|
| + this.prepareElement();
|
| + if (!this.ownerDocument.isStagingDocument) {
|
| + this.makeElementReady();
|
| + }
|
| + },
|
| +
|
| + // system entry point, do not override
|
| + prepareElement: function() {
|
| + if (this._elementPrepared) {
|
| + console.warn('Element already prepared', this.localName);
|
| + return;
|
| + }
|
| + this._elementPrepared = true;
|
| + // storage for shadowRoots info
|
| + this.shadowRoots = {};
|
| + // install property observers
|
| + this.createPropertyObserver();
|
| + this.openPropertyObserver();
|
| + // install boilerplate attributes
|
| + this.copyInstanceAttributes();
|
| + // process input attributes
|
| + this.takeAttributes();
|
| + // add event listeners
|
| + this.addHostListeners();
|
| + },
|
| +
|
| + // system entry point, do not override
|
| + makeElementReady: function() {
|
| + if (this._readied) {
|
| + return;
|
| + }
|
| + this._readied = true;
|
| + this.createComputedProperties();
|
| + this.parseDeclarations(this.__proto__);
|
| + // NOTE: Support use of the `unresolved` attribute to help polyfill
|
| + // custom elements' `:unresolved` feature.
|
| + this.removeAttribute('unresolved');
|
| + // user entry point
|
| + this.ready();
|
| + },
|
| +
|
| + /**
|
| + * Low-level lifecycle method called as part of standard Custom Elements
|
| + * operation. Polymer implements this method to provide basic default
|
| + * functionality. For custom tasks in your element, implement `attributeChanged`
|
| + * instead, which is called immediately after `attributeChangedCallback`.
|
| + *
|
| + * @method attributeChangedCallback
|
| + */
|
| + attributeChangedCallback: function(name, oldValue) {
|
| + // TODO(sjmiles): adhoc filter
|
| + if (name !== 'class' && name !== 'style') {
|
| + this.attributeToProperty(name, this.getAttribute(name));
|
| + }
|
| + if (this.attributeChanged) {
|
| + this.attributeChanged.apply(this, arguments);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Low-level lifecycle method called as part of standard Custom Elements
|
| + * operation. Polymer implements this method to provide basic default
|
| + * functionality. For custom create-time tasks, implement `attached`
|
| + * instead, which is called immediately after `attachedCallback`.
|
| + *
|
| + * @method attachedCallback
|
| + */
|
| + attachedCallback: function() {
|
| + // when the element is attached, prevent it from unbinding.
|
| + this.cancelUnbindAll();
|
| + // invoke user action
|
| + if (this.attached) {
|
| + this.attached();
|
| + }
|
| + if (!this.hasBeenAttached) {
|
| + this.hasBeenAttached = true;
|
| + if (this.domReady) {
|
| + this.async('domReady');
|
| + }
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Implement to access custom elements in dom descendants, ancestors,
|
| + * or siblings. Because custom elements upgrade in document order,
|
| + * elements accessed in `ready` or `attached` may not be upgraded. When
|
| + * `domReady` is called, all registered custom elements are guaranteed
|
| + * to have been upgraded.
|
| + *
|
| + * @method domReady
|
| + */
|
| +
|
| + /**
|
| + * Low-level lifecycle method called as part of standard Custom Elements
|
| + * operation. Polymer implements this method to provide basic default
|
| + * functionality. For custom create-time tasks, implement `detached`
|
| + * instead, which is called immediately after `detachedCallback`.
|
| + *
|
| + * @method detachedCallback
|
| + */
|
| + detachedCallback: function() {
|
| + if (!this.preventDispose) {
|
| + this.asyncUnbindAll();
|
| + }
|
| + // invoke user action
|
| + if (this.detached) {
|
| + this.detached();
|
| + }
|
| + // TODO(sorvell): bc
|
| + if (this.leftView) {
|
| + this.leftView();
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Walks the prototype-chain of this element and allows specific
|
| + * classes a chance to process static declarations.
|
| + *
|
| + * In particular, each polymer-element has it's own `template`.
|
| + * `parseDeclarations` is used to accumulate all element `template`s
|
| + * from an inheritance chain.
|
| + *
|
| + * `parseDeclaration` static methods implemented in the chain are called
|
| + * recursively, oldest first, with the `<polymer-element>` associated
|
| + * with the current prototype passed as an argument.
|
| + *
|
| + * An element may override this method to customize shadow-root generation.
|
| + *
|
| + * @method parseDeclarations
|
| + */
|
| + parseDeclarations: function(p) {
|
| + if (p && p.element) {
|
| + this.parseDeclarations(p.__proto__);
|
| + p.parseDeclaration.call(this, p.element);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Perform init-time actions based on static information in the
|
| + * `<polymer-element>` instance argument.
|
| + *
|
| + * For example, the standard implementation locates the template associated
|
| + * with the given `<polymer-element>` and stamps it into a shadow-root to
|
| + * implement shadow inheritance.
|
| + *
|
| + * An element may override this method for custom behavior.
|
| + *
|
| + * @method parseDeclaration
|
| + */
|
| + parseDeclaration: function(elementElement) {
|
| + var template = this.fetchTemplate(elementElement);
|
| + if (template) {
|
| + var root = this.shadowFromTemplate(template);
|
| + this.shadowRoots[elementElement.name] = root;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Given a `<polymer-element>`, find an associated template (if any) to be
|
| + * used for shadow-root generation.
|
| + *
|
| + * An element may override this method for custom behavior.
|
| + *
|
| + * @method fetchTemplate
|
| + */
|
| + fetchTemplate: function(elementElement) {
|
| + return elementElement.querySelector('template');
|
| + },
|
| +
|
| + /**
|
| + * Create a shadow-root in this host and stamp `template` as it's
|
| + * content.
|
| + *
|
| + * An element may override this method for custom behavior.
|
| + *
|
| + * @method shadowFromTemplate
|
| + */
|
| + shadowFromTemplate: function(template) {
|
| + if (template) {
|
| + // make a shadow root
|
| + var root = this.createShadowRoot();
|
| + // stamp template
|
| + // which includes parsing and applying MDV bindings before being
|
| + // inserted (to avoid {{}} in attribute values)
|
| + // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
|
| + var dom = this.instanceTemplate(template);
|
| + // append to shadow dom
|
| + root.appendChild(dom);
|
| + // perform post-construction initialization tasks on shadow root
|
| + this.shadowRootReady(root, template);
|
| + // return the created shadow root
|
| + return root;
|
| + }
|
| + },
|
| +
|
| + // utility function that stamps a <template> into light-dom
|
| + lightFromTemplate: function(template, refNode) {
|
| + if (template) {
|
| + // TODO(sorvell): mark this element as an eventController so that
|
| + // event listeners on bound nodes inside it will be called on it.
|
| + // Note, the expectation here is that events on all descendants
|
| + // should be handled by this element.
|
| + this.eventController = this;
|
| + // stamp template
|
| + // which includes parsing and applying MDV bindings before being
|
| + // inserted (to avoid {{}} in attribute values)
|
| + // e.g. to prevent <img src="images/{{icon}}"> from generating a 404.
|
| + var dom = this.instanceTemplate(template);
|
| + // append to shadow dom
|
| + if (refNode) {
|
| + this.insertBefore(dom, refNode);
|
| + } else {
|
| + this.appendChild(dom);
|
| + }
|
| + // perform post-construction initialization tasks on ahem, light root
|
| + this.shadowRootReady(this);
|
| + // return the created shadow root
|
| + return dom;
|
| + }
|
| + },
|
| +
|
| + shadowRootReady: function(root) {
|
| + // locate nodes with id and store references to them in this.$ hash
|
| + this.marshalNodeReferences(root);
|
| + },
|
| +
|
| + // locate nodes with id and store references to them in this.$ hash
|
| + marshalNodeReferences: function(root) {
|
| + // establish $ instance variable
|
| + var $ = this.$ = this.$ || {};
|
| + // populate $ from nodes with ID from the LOCAL tree
|
| + if (root) {
|
| + var n$ = root.querySelectorAll("[id]");
|
| + for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) {
|
| + $[n.id] = n;
|
| + };
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Register a one-time callback when a child-list or sub-tree mutation
|
| + * occurs on node.
|
| + *
|
| + * For persistent callbacks, call onMutation from your listener.
|
| + *
|
| + * @method onMutation
|
| + * @param Node {Node} node Node to watch for mutations.
|
| + * @param Function {Function} listener Function to call on mutation. The function is invoked as `listener.call(this, observer, mutations);` where `observer` is the MutationObserver that triggered the notification, and `mutations` is the native mutation list.
|
| + */
|
| + onMutation: function(node, listener) {
|
| + var observer = new MutationObserver(function(mutations) {
|
| + listener.call(this, observer, mutations);
|
| + observer.disconnect();
|
| + }.bind(this));
|
| + observer.observe(node, {childList: true, subtree: true});
|
| + }
|
| + };
|
| +
|
| + /**
|
| + * @class Polymer
|
| + */
|
| +
|
| + /**
|
| + * Returns true if the object includes <a href="#polymer-base">polymer-base</a> in it's prototype chain.
|
| + *
|
| + * @method isBase
|
| + * @param Object {Object} object Object to test.
|
| + * @type Boolean
|
| + */
|
| + function isBase(object) {
|
| + return object.hasOwnProperty('PolymerBase')
|
| + }
|
| +
|
| + // name a base constructor for dev tools
|
| +
|
| + /**
|
| + * The Polymer base-class constructor.
|
| + *
|
| + * @property Base
|
| + * @type Function
|
| + */
|
| + function PolymerBase() {};
|
| + PolymerBase.prototype = base;
|
| + base.constructor = PolymerBase;
|
| +
|
| + // exports
|
| +
|
| + scope.Base = PolymerBase;
|
| + scope.isBase = isBase;
|
| + scope.api.instance.base = base;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| + var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
|
| +
|
| + // magic words
|
| +
|
| + var STYLE_SCOPE_ATTRIBUTE = 'element';
|
| + var STYLE_CONTROLLER_SCOPE = 'controller';
|
| +
|
| + var styles = {
|
| + STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE,
|
| + /**
|
| + * Installs external stylesheets and <style> elements with the attribute
|
| + * polymer-scope='controller' into the scope of element. This is intended
|
| + * to be a called during custom element construction.
|
| + */
|
| + installControllerStyles: function() {
|
| + // apply controller styles, but only if they are not yet applied
|
| + var scope = this.findStyleScope();
|
| + if (scope && !this.scopeHasNamedStyle(scope, this.localName)) {
|
| + // allow inherited controller styles
|
| + var proto = getPrototypeOf(this), cssText = '';
|
| + while (proto && proto.element) {
|
| + cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE);
|
| + proto = getPrototypeOf(proto);
|
| + }
|
| + if (cssText) {
|
| + this.installScopeCssText(cssText, scope);
|
| + }
|
| + }
|
| + },
|
| + installScopeStyle: function(style, name, scope) {
|
| + var scope = scope || this.findStyleScope(), name = name || '';
|
| + if (scope && !this.scopeHasNamedStyle(scope, this.localName + name)) {
|
| + var cssText = '';
|
| + if (style instanceof Array) {
|
| + for (var i=0, l=style.length, s; (i<l) && (s=style[i]); i++) {
|
| + cssText += s.textContent + '\n\n';
|
| + }
|
| + } else {
|
| + cssText = style.textContent;
|
| + }
|
| + this.installScopeCssText(cssText, scope, name);
|
| + }
|
| + },
|
| + installScopeCssText: function(cssText, scope, name) {
|
| + scope = scope || this.findStyleScope();
|
| + name = name || '';
|
| + if (!scope) {
|
| + return;
|
| + }
|
| + if (hasShadowDOMPolyfill) {
|
| + cssText = shimCssText(cssText, scope.host);
|
| + }
|
| + var style = this.element.cssTextToScopeStyle(cssText,
|
| + STYLE_CONTROLLER_SCOPE);
|
| + Polymer.applyStyleToScope(style, scope);
|
| + // cache that this style has been applied
|
| + this.styleCacheForScope(scope)[this.localName + name] = true;
|
| + },
|
| + findStyleScope: function(node) {
|
| + // find the shadow root that contains this element
|
| + var n = node || this;
|
| + while (n.parentNode) {
|
| + n = n.parentNode;
|
| + }
|
| + return n;
|
| + },
|
| + scopeHasNamedStyle: function(scope, name) {
|
| + var cache = this.styleCacheForScope(scope);
|
| + return cache[name];
|
| + },
|
| + styleCacheForScope: function(scope) {
|
| + if (hasShadowDOMPolyfill) {
|
| + var scopeName = scope.host ? scope.host.localName : scope.localName;
|
| + return polyfillScopeStyleCache[scopeName] || (polyfillScopeStyleCache[scopeName] = {});
|
| + } else {
|
| + return scope._scopeStyles = (scope._scopeStyles || {});
|
| + }
|
| + }
|
| + };
|
| +
|
| + var polyfillScopeStyleCache = {};
|
| +
|
| + // NOTE: use raw prototype traversal so that we ensure correct traversal
|
| + // on platforms where the protoype chain is simulated via __proto__ (IE10)
|
| + function getPrototypeOf(prototype) {
|
| + return prototype.__proto__;
|
| + }
|
| +
|
| + function shimCssText(cssText, host) {
|
| + var name = '', is = false;
|
| + if (host) {
|
| + name = host.localName;
|
| + is = host.hasAttribute('is');
|
| + }
|
| + var selector = WebComponents.ShadowCSS.makeScopeSelector(name, is);
|
| + return WebComponents.ShadowCSS.shimCssText(cssText, selector);
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.api.instance.styles = styles;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var extend = scope.extend;
|
| + var api = scope.api;
|
| +
|
| + // imperative implementation: Polymer()
|
| +
|
| + // specify an 'own' prototype for tag `name`
|
| + function element(name, prototype) {
|
| + if (typeof name !== 'string') {
|
| + var script = prototype || document._currentScript;
|
| + prototype = name;
|
| + name = script && script.parentNode && script.parentNode.getAttribute ?
|
| + script.parentNode.getAttribute('name') : '';
|
| + if (!name) {
|
| + throw 'Element name could not be inferred.';
|
| + }
|
| + }
|
| + if (getRegisteredPrototype(name)) {
|
| + throw 'Already registered (Polymer) prototype for element ' + name;
|
| + }
|
| + // cache the prototype
|
| + registerPrototype(name, prototype);
|
| + // notify the registrar waiting for 'name', if any
|
| + notifyPrototype(name);
|
| + }
|
| +
|
| + // async prototype source
|
| +
|
| + function waitingForPrototype(name, client) {
|
| + waitPrototype[name] = client;
|
| + }
|
| +
|
| + var waitPrototype = {};
|
| +
|
| + function notifyPrototype(name) {
|
| + if (waitPrototype[name]) {
|
| + waitPrototype[name].registerWhenReady();
|
| + delete waitPrototype[name];
|
| + }
|
| + }
|
| +
|
| + // utility and bookkeeping
|
| +
|
| + // maps tag names to prototypes, as registered with
|
| + // Polymer. Prototypes associated with a tag name
|
| + // using document.registerElement are available from
|
| + // HTMLElement.getPrototypeForTag().
|
| + // If an element was fully registered by Polymer, then
|
| + // Polymer.getRegisteredPrototype(name) ===
|
| + // HTMLElement.getPrototypeForTag(name)
|
| +
|
| + var prototypesByName = {};
|
| +
|
| + function registerPrototype(name, prototype) {
|
| + return prototypesByName[name] = prototype || {};
|
| + }
|
| +
|
| + function getRegisteredPrototype(name) {
|
| + return prototypesByName[name];
|
| + }
|
| +
|
| + function instanceOfType(element, type) {
|
| + if (typeof type !== 'string') {
|
| + return false;
|
| + }
|
| + var proto = HTMLElement.getPrototypeForTag(type);
|
| + var ctor = proto && proto.constructor;
|
| + if (!ctor) {
|
| + return false;
|
| + }
|
| + if (CustomElements.instanceof) {
|
| + return CustomElements.instanceof(element, ctor);
|
| + }
|
| + return element instanceof ctor;
|
| + }
|
| +
|
| + // exports
|
| +
|
| + scope.getRegisteredPrototype = getRegisteredPrototype;
|
| + scope.waitingForPrototype = waitingForPrototype;
|
| + scope.instanceOfType = instanceOfType;
|
| +
|
| + // namespace shenanigans so we can expose our scope on the registration
|
| + // function
|
| +
|
| + // make window.Polymer reference `element()`
|
| +
|
| + window.Polymer = element;
|
| +
|
| + // TODO(sjmiles): find a way to do this that is less terrible
|
| + // copy window.Polymer properties onto `element()`
|
| +
|
| + extend(Polymer, scope);
|
| +
|
| + // Under the HTMLImports polyfill, scripts in the main document
|
| + // do not block on imports; we want to allow calls to Polymer in the main
|
| + // document. WebComponents collects those calls until we can process them, which
|
| + // we do here.
|
| +
|
| + if (WebComponents.consumeDeclarations) {
|
| + WebComponents.consumeDeclarations(function(declarations) {
|
| + if (declarations) {
|
| + for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) {
|
| + element.apply(null, d);
|
| + }
|
| + }
|
| + });
|
| + }
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| +/**
|
| + * @class polymer-base
|
| + */
|
| +
|
| + /**
|
| + * Resolve a url path to be relative to a `base` url. If unspecified, `base`
|
| + * defaults to the element's ownerDocument url. Can be used to resolve
|
| + * paths from element's in templates loaded in HTMLImports to be relative
|
| + * to the document containing the element. Polymer automatically does this for
|
| + * url attributes in element templates; however, if a url, for
|
| + * example, contains a binding, then `resolvePath` can be used to ensure it is
|
| + * relative to the element document. For example, in an element's template,
|
| + *
|
| + * <a href="{{resolvePath(path)}}">Resolved</a>
|
| + *
|
| + * @method resolvePath
|
| + * @param {String} url Url path to resolve.
|
| + * @param {String} base Optional base url against which to resolve, defaults
|
| + * to the element's ownerDocument url.
|
| + * returns {String} resolved url.
|
| + */
|
| +
|
| +var path = {
|
| + resolveElementPaths: function(node) {
|
| + Polymer.urlResolver.resolveDom(node);
|
| + },
|
| + addResolvePathApi: function() {
|
| + // let assetpath attribute modify the resolve path
|
| + var assetPath = this.getAttribute('assetpath') || '';
|
| + var root = new URL(assetPath, this.ownerDocument.baseURI);
|
| + this.prototype.resolvePath = function(urlPath, base) {
|
| + var u = new URL(urlPath, base || root);
|
| + return u.href;
|
| + };
|
| + }
|
| +};
|
| +
|
| +// exports
|
| +scope.api.declaration.path = path;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| + var api = scope.api.instance.styles;
|
| + var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE;
|
| +
|
| + var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
|
| +
|
| + // magic words
|
| +
|
| + var STYLE_SELECTOR = 'style';
|
| + var STYLE_LOADABLE_MATCH = '@import';
|
| + var SHEET_SELECTOR = 'link[rel=stylesheet]';
|
| + var STYLE_GLOBAL_SCOPE = 'global';
|
| + var SCOPE_ATTR = 'polymer-scope';
|
| +
|
| + var styles = {
|
| + // returns true if resources are loading
|
| + loadStyles: function(callback) {
|
| + var template = this.fetchTemplate();
|
| + var content = template && this.templateContent();
|
| + if (content) {
|
| + this.convertSheetsToStyles(content);
|
| + var styles = this.findLoadableStyles(content);
|
| + if (styles.length) {
|
| + var templateUrl = template.ownerDocument.baseURI;
|
| + return Polymer.styleResolver.loadStyles(styles, templateUrl, callback);
|
| + }
|
| + }
|
| + if (callback) {
|
| + callback();
|
| + }
|
| + },
|
| + convertSheetsToStyles: function(root) {
|
| + var s$ = root.querySelectorAll(SHEET_SELECTOR);
|
| + for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) {
|
| + c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI),
|
| + this.ownerDocument);
|
| + this.copySheetAttributes(c, s);
|
| + s.parentNode.replaceChild(c, s);
|
| + }
|
| + },
|
| + copySheetAttributes: function(style, link) {
|
| + for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) {
|
| + if (a.name !== 'rel' && a.name !== 'href') {
|
| + style.setAttribute(a.name, a.value);
|
| + }
|
| + }
|
| + },
|
| + findLoadableStyles: function(root) {
|
| + var loadables = [];
|
| + if (root) {
|
| + var s$ = root.querySelectorAll(STYLE_SELECTOR);
|
| + for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
|
| + if (s.textContent.match(STYLE_LOADABLE_MATCH)) {
|
| + loadables.push(s);
|
| + }
|
| + }
|
| + }
|
| + return loadables;
|
| + },
|
| + /**
|
| + * Install external stylesheets loaded in <polymer-element> elements into the
|
| + * element's template.
|
| + * @param elementElement The <element> element to style.
|
| + */
|
| + installSheets: function() {
|
| + this.cacheSheets();
|
| + this.cacheStyles();
|
| + this.installLocalSheets();
|
| + this.installGlobalStyles();
|
| + },
|
| + /**
|
| + * Remove all sheets from element and store for later use.
|
| + */
|
| + cacheSheets: function() {
|
| + this.sheets = this.findNodes(SHEET_SELECTOR);
|
| + this.sheets.forEach(function(s) {
|
| + if (s.parentNode) {
|
| + s.parentNode.removeChild(s);
|
| + }
|
| + });
|
| + },
|
| + cacheStyles: function() {
|
| + this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']');
|
| + this.styles.forEach(function(s) {
|
| + if (s.parentNode) {
|
| + s.parentNode.removeChild(s);
|
| + }
|
| + });
|
| + },
|
| + /**
|
| + * Takes external stylesheets loaded in an <element> element and moves
|
| + * their content into a <style> element inside the <element>'s template.
|
| + * The sheet is then removed from the <element>. This is done only so
|
| + * that if the element is loaded in the main document, the sheet does
|
| + * not become active.
|
| + * Note, ignores sheets with the attribute 'polymer-scope'.
|
| + * @param elementElement The <element> element to style.
|
| + */
|
| + installLocalSheets: function () {
|
| + var sheets = this.sheets.filter(function(s) {
|
| + return !s.hasAttribute(SCOPE_ATTR);
|
| + });
|
| + var content = this.templateContent();
|
| + if (content) {
|
| + var cssText = '';
|
| + sheets.forEach(function(sheet) {
|
| + cssText += cssTextFromSheet(sheet) + '\n';
|
| + });
|
| + if (cssText) {
|
| + var style = createStyleElement(cssText, this.ownerDocument);
|
| + content.insertBefore(style, content.firstChild);
|
| + }
|
| + }
|
| + },
|
| + findNodes: function(selector, matcher) {
|
| + var nodes = this.querySelectorAll(selector).array();
|
| + var content = this.templateContent();
|
| + if (content) {
|
| + var templateNodes = content.querySelectorAll(selector).array();
|
| + nodes = nodes.concat(templateNodes);
|
| + }
|
| + return matcher ? nodes.filter(matcher) : nodes;
|
| + },
|
| + /**
|
| + * Promotes external stylesheets and <style> elements with the attribute
|
| + * polymer-scope='global' into global scope.
|
| + * This is particularly useful for defining @keyframe rules which
|
| + * currently do not function in scoped or shadow style elements.
|
| + * (See wkb.ug/72462)
|
| + * @param elementElement The <element> element to style.
|
| + */
|
| + // TODO(sorvell): remove when wkb.ug/72462 is addressed.
|
| + installGlobalStyles: function() {
|
| + var style = this.styleForScope(STYLE_GLOBAL_SCOPE);
|
| + applyStyleToScope(style, document.head);
|
| + },
|
| + cssTextForScope: function(scopeDescriptor) {
|
| + var cssText = '';
|
| + // handle stylesheets
|
| + var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']';
|
| + var matcher = function(s) {
|
| + return matchesSelector(s, selector);
|
| + };
|
| + var sheets = this.sheets.filter(matcher);
|
| + sheets.forEach(function(sheet) {
|
| + cssText += cssTextFromSheet(sheet) + '\n\n';
|
| + });
|
| + // handle cached style elements
|
| + var styles = this.styles.filter(matcher);
|
| + styles.forEach(function(style) {
|
| + cssText += style.textContent + '\n\n';
|
| + });
|
| + return cssText;
|
| + },
|
| + styleForScope: function(scopeDescriptor) {
|
| + var cssText = this.cssTextForScope(scopeDescriptor);
|
| + return this.cssTextToScopeStyle(cssText, scopeDescriptor);
|
| + },
|
| + cssTextToScopeStyle: function(cssText, scopeDescriptor) {
|
| + if (cssText) {
|
| + var style = createStyleElement(cssText);
|
| + style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') +
|
| + '-' + scopeDescriptor);
|
| + return style;
|
| + }
|
| + }
|
| + };
|
| +
|
| + function importRuleForSheet(sheet, baseUrl) {
|
| + var href = new URL(sheet.getAttribute('href'), baseUrl).href;
|
| + return '@import \'' + href + '\';';
|
| + }
|
| +
|
| + function applyStyleToScope(style, scope) {
|
| + if (style) {
|
| + if (scope === document) {
|
| + scope = document.head;
|
| + }
|
| + if (hasShadowDOMPolyfill) {
|
| + scope = document.head;
|
| + }
|
| + // TODO(sorvell): necessary for IE
|
| + // see https://connect.microsoft.com/IE/feedback/details/790212/
|
| + // cloning-a-style-element-and-adding-to-document-produces
|
| + // -unexpected-result#details
|
| + // var clone = style.cloneNode(true);
|
| + var clone = createStyleElement(style.textContent);
|
| + var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE);
|
| + if (attr) {
|
| + clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr);
|
| + }
|
| + // TODO(sorvell): probably too brittle; try to figure out
|
| + // where to put the element.
|
| + var refNode = scope.firstElementChild;
|
| + if (scope === document.head) {
|
| + var selector = 'style[' + STYLE_SCOPE_ATTRIBUTE + ']';
|
| + var s$ = document.head.querySelectorAll(selector);
|
| + if (s$.length) {
|
| + refNode = s$[s$.length-1].nextElementSibling;
|
| + }
|
| + }
|
| + scope.insertBefore(clone, refNode);
|
| + }
|
| + }
|
| +
|
| + function createStyleElement(cssText, scope) {
|
| + scope = scope || document;
|
| + scope = scope.createElement ? scope : scope.ownerDocument;
|
| + var style = scope.createElement('style');
|
| + style.textContent = cssText;
|
| + return style;
|
| + }
|
| +
|
| + function cssTextFromSheet(sheet) {
|
| + return (sheet && sheet.__resource) || '';
|
| + }
|
| +
|
| + function matchesSelector(node, inSelector) {
|
| + if (matches) {
|
| + return matches.call(node, inSelector);
|
| + }
|
| + }
|
| + var p = HTMLElement.prototype;
|
| + var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector
|
| + || p.mozMatchesSelector;
|
| +
|
| + // exports
|
| +
|
| + scope.api.declaration.styles = styles;
|
| + scope.applyStyleToScope = applyStyleToScope;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var log = window.WebComponents ? WebComponents.flags.log : {};
|
| + var api = scope.api.instance.events;
|
| + var EVENT_PREFIX = api.EVENT_PREFIX;
|
| +
|
| + var mixedCaseEventTypes = {};
|
| + [
|
| + 'webkitAnimationStart',
|
| + 'webkitAnimationEnd',
|
| + 'webkitTransitionEnd',
|
| + 'DOMFocusOut',
|
| + 'DOMFocusIn',
|
| + 'DOMMouseScroll'
|
| + ].forEach(function(e) {
|
| + mixedCaseEventTypes[e.toLowerCase()] = e;
|
| + });
|
| +
|
| + // polymer-element declarative api: events feature
|
| + var events = {
|
| + parseHostEvents: function() {
|
| + // our delegates map
|
| + var delegates = this.prototype.eventDelegates;
|
| + // extract data from attributes into delegates
|
| + this.addAttributeDelegates(delegates);
|
| + },
|
| + addAttributeDelegates: function(delegates) {
|
| + // for each attribute
|
| + for (var i=0, a; a=this.attributes[i]; i++) {
|
| + // does it have magic marker identifying it as an event delegate?
|
| + if (this.hasEventPrefix(a.name)) {
|
| + // if so, add the info to delegates
|
| + delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '')
|
| + .replace('}}', '').trim();
|
| + }
|
| + }
|
| + },
|
| + // starts with 'on-'
|
| + hasEventPrefix: function (n) {
|
| + return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-');
|
| + },
|
| + removeEventPrefix: function(n) {
|
| + return n.slice(prefixLength);
|
| + },
|
| + findController: function(node) {
|
| + while (node.parentNode) {
|
| + if (node.eventController) {
|
| + return node.eventController;
|
| + }
|
| + node = node.parentNode;
|
| + }
|
| + return node.host;
|
| + },
|
| + getEventHandler: function(controller, target, method) {
|
| + var events = this;
|
| + return function(e) {
|
| + if (!controller || !controller.PolymerBase) {
|
| + controller = events.findController(target);
|
| + }
|
| +
|
| + var args = [e, e.detail, e.currentTarget];
|
| + controller.dispatchMethod(controller, method, args);
|
| + };
|
| + },
|
| + prepareEventBinding: function(pathString, name, node) {
|
| + if (!this.hasEventPrefix(name))
|
| + return;
|
| +
|
| + var eventType = this.removeEventPrefix(name);
|
| + eventType = mixedCaseEventTypes[eventType] || eventType;
|
| +
|
| + var events = this;
|
| +
|
| + return function(model, node, oneTime) {
|
| + var handler = events.getEventHandler(undefined, node, pathString);
|
| + PolymerGestures.addEventListener(node, eventType, handler);
|
| +
|
| + if (oneTime)
|
| + return;
|
| +
|
| + // TODO(rafaelw): This is really pointless work. Aside from the cost
|
| + // of these allocations, NodeBind is going to setAttribute back to its
|
| + // current value. Fixing this would mean changing the TemplateBinding
|
| + // binding delegate API.
|
| + function bindingValue() {
|
| + return '{{ ' + pathString + ' }}';
|
| + }
|
| +
|
| + return {
|
| + open: bindingValue,
|
| + discardChanges: bindingValue,
|
| + close: function() {
|
| + PolymerGestures.removeEventListener(node, eventType, handler);
|
| + }
|
| + };
|
| + };
|
| + }
|
| + };
|
| +
|
| + var prefixLength = EVENT_PREFIX.length;
|
| +
|
| + // exports
|
| + scope.api.declaration.events = events;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // element api
|
| +
|
| + var observationBlacklist = ['attribute'];
|
| +
|
| + var properties = {
|
| + inferObservers: function(prototype) {
|
| + // called before prototype.observe is chained to inherited object
|
| + var observe = prototype.observe, property;
|
| + for (var n in prototype) {
|
| + if (n.slice(-7) === 'Changed') {
|
| + property = n.slice(0, -7);
|
| + if (this.canObserveProperty(property)) {
|
| + if (!observe) {
|
| + observe = (prototype.observe = {});
|
| + }
|
| + observe[property] = observe[property] || n;
|
| + }
|
| + }
|
| + }
|
| + },
|
| + canObserveProperty: function(property) {
|
| + return (observationBlacklist.indexOf(property) < 0);
|
| + },
|
| + explodeObservers: function(prototype) {
|
| + // called before prototype.observe is chained to inherited object
|
| + var o = prototype.observe;
|
| + if (o) {
|
| + var exploded = {};
|
| + for (var n in o) {
|
| + var names = n.split(' ');
|
| + for (var i=0, ni; ni=names[i]; i++) {
|
| + exploded[ni] = o[n];
|
| + }
|
| + }
|
| + prototype.observe = exploded;
|
| + }
|
| + },
|
| + optimizePropertyMaps: function(prototype) {
|
| + if (prototype.observe) {
|
| + // construct name list
|
| + var a = prototype._observeNames = [];
|
| + for (var n in prototype.observe) {
|
| + var names = n.split(' ');
|
| + for (var i=0, ni; ni=names[i]; i++) {
|
| + a.push(ni);
|
| + }
|
| + }
|
| + }
|
| + if (prototype.publish) {
|
| + // construct name list
|
| + var a = prototype._publishNames = [];
|
| + for (var n in prototype.publish) {
|
| + a.push(n);
|
| + }
|
| + }
|
| + if (prototype.computed) {
|
| + // construct name list
|
| + var a = prototype._computedNames = [];
|
| + for (var n in prototype.computed) {
|
| + a.push(n);
|
| + }
|
| + }
|
| + },
|
| + publishProperties: function(prototype, base) {
|
| + // if we have any properties to publish
|
| + var publish = prototype.publish;
|
| + if (publish) {
|
| + // transcribe `publish` entries onto own prototype
|
| + this.requireProperties(publish, prototype, base);
|
| + // warn and remove accessor names that are broken on some browsers
|
| + this.filterInvalidAccessorNames(publish);
|
| + // construct map of lower-cased property names
|
| + prototype._publishLC = this.lowerCaseMap(publish);
|
| + }
|
| + var computed = prototype.computed;
|
| + if (computed) {
|
| + // warn and remove accessor names that are broken on some browsers
|
| + this.filterInvalidAccessorNames(computed);
|
| + }
|
| + },
|
| + // Publishing/computing a property where the name might conflict with a
|
| + // browser property is not currently supported to help users of Polymer
|
| + // avoid browser bugs:
|
| + //
|
| + // https://code.google.com/p/chromium/issues/detail?id=43394
|
| + // https://bugs.webkit.org/show_bug.cgi?id=49739
|
| + //
|
| + // We can lift this restriction when those bugs are fixed.
|
| + filterInvalidAccessorNames: function(propertyNames) {
|
| + for (var name in propertyNames) {
|
| + // Check if the name is in our blacklist.
|
| + if (this.propertyNameBlacklist[name]) {
|
| + console.warn('Cannot define property "' + name + '" for element "' +
|
| + this.name + '" because it has the same name as an HTMLElement ' +
|
| + 'property, and not all browsers support overriding that. ' +
|
| + 'Consider giving it a different name.');
|
| + // Remove the invalid accessor from the list.
|
| + delete propertyNames[name];
|
| + }
|
| + }
|
| + },
|
| + //
|
| + // `name: value` entries in the `publish` object may need to generate
|
| + // matching properties on the prototype.
|
| + //
|
| + // Values that are objects may have a `reflect` property, which
|
| + // signals that the value describes property control metadata.
|
| + // In metadata objects, the prototype default value (if any)
|
| + // is encoded in the `value` property.
|
| + //
|
| + // publish: {
|
| + // foo: 5,
|
| + // bar: {value: true, reflect: true},
|
| + // zot: {}
|
| + // }
|
| + //
|
| + // `reflect` metadata property controls whether changes to the property
|
| + // are reflected back to the attribute (default false).
|
| + //
|
| + // A value is stored on the prototype unless it's === `undefined`,
|
| + // in which case the base chain is checked for a value.
|
| + // If the basal value is also undefined, `null` is stored on the prototype.
|
| + //
|
| + // The reflection data is stored on another prototype object, `reflect`
|
| + // which also can be specified directly.
|
| + //
|
| + // reflect: {
|
| + // foo: true
|
| + // }
|
| + //
|
| + requireProperties: function(propertyInfos, prototype, base) {
|
| + // per-prototype storage for reflected properties
|
| + prototype.reflect = prototype.reflect || {};
|
| + // ensure a prototype value for each property
|
| + // and update the property's reflect to attribute status
|
| + for (var n in propertyInfos) {
|
| + var value = propertyInfos[n];
|
| + // value has metadata if it has a `reflect` property
|
| + if (value && value.reflect !== undefined) {
|
| + prototype.reflect[n] = Boolean(value.reflect);
|
| + value = value.value;
|
| + }
|
| + // only set a value if one is specified
|
| + if (value !== undefined) {
|
| + prototype[n] = value;
|
| + }
|
| + }
|
| + },
|
| + lowerCaseMap: function(properties) {
|
| + var map = {};
|
| + for (var n in properties) {
|
| + map[n.toLowerCase()] = n;
|
| + }
|
| + return map;
|
| + },
|
| + createPropertyAccessor: function(name, ignoreWrites) {
|
| + var proto = this.prototype;
|
| +
|
| + var privateName = name + '_';
|
| + var privateObservable = name + 'Observable_';
|
| + proto[privateName] = proto[name];
|
| +
|
| + Object.defineProperty(proto, name, {
|
| + get: function() {
|
| + var observable = this[privateObservable];
|
| + if (observable)
|
| + observable.deliver();
|
| +
|
| + return this[privateName];
|
| + },
|
| + set: function(value) {
|
| + if (ignoreWrites) {
|
| + return this[privateName];
|
| + }
|
| +
|
| + var observable = this[privateObservable];
|
| + if (observable) {
|
| + observable.setValue(value);
|
| + return;
|
| + }
|
| +
|
| + var oldValue = this[privateName];
|
| + this[privateName] = value;
|
| + this.emitPropertyChangeRecord(name, value, oldValue);
|
| +
|
| + return value;
|
| + },
|
| + configurable: true
|
| + });
|
| + },
|
| + createPropertyAccessors: function(prototype) {
|
| + var n$ = prototype._computedNames;
|
| + if (n$ && n$.length) {
|
| + for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
|
| + this.createPropertyAccessor(n, true);
|
| + }
|
| + }
|
| + var n$ = prototype._publishNames;
|
| + if (n$ && n$.length) {
|
| + for (var i=0, l=n$.length, n, fn; (i<l) && (n=n$[i]); i++) {
|
| + // If the property is computed and published, the accessor is created
|
| + // above.
|
| + if (!prototype.computed || !prototype.computed[n]) {
|
| + this.createPropertyAccessor(n);
|
| + }
|
| + }
|
| + }
|
| + },
|
| + // This list contains some property names that people commonly want to use,
|
| + // but won't work because of Chrome/Safari bugs. It isn't an exhaustive
|
| + // list. In particular it doesn't contain any property names found on
|
| + // subtypes of HTMLElement (e.g. name, value). Rather it attempts to catch
|
| + // some common cases.
|
| + propertyNameBlacklist: {
|
| + children: 1,
|
| + 'class': 1,
|
| + id: 1,
|
| + hidden: 1,
|
| + style: 1,
|
| + title: 1,
|
| + }
|
| + };
|
| +
|
| + // exports
|
| +
|
| + scope.api.declaration.properties = properties;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // magic words
|
| +
|
| + var ATTRIBUTES_ATTRIBUTE = 'attributes';
|
| + var ATTRIBUTES_REGEX = /\s|,/;
|
| +
|
| + // attributes api
|
| +
|
| + var attributes = {
|
| +
|
| + inheritAttributesObjects: function(prototype) {
|
| + // chain our lower-cased publish map to the inherited version
|
| + this.inheritObject(prototype, 'publishLC');
|
| + // chain our instance attributes map to the inherited version
|
| + this.inheritObject(prototype, '_instanceAttributes');
|
| + },
|
| +
|
| + publishAttributes: function(prototype, base) {
|
| + // merge names from 'attributes' attribute into the 'publish' object
|
| + var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE);
|
| + if (attributes) {
|
| + // create a `publish` object if needed.
|
| + // the `publish` object is only relevant to this prototype, the
|
| + // publishing logic in `declaration/properties.js` is responsible for
|
| + // managing property values on the prototype chain.
|
| + // TODO(sjmiles): the `publish` object is later chained to it's
|
| + // ancestor object, presumably this is only for
|
| + // reflection or other non-library uses.
|
| + var publish = prototype.publish || (prototype.publish = {});
|
| + // names='a b c' or names='a,b,c'
|
| + var names = attributes.split(ATTRIBUTES_REGEX);
|
| + // record each name for publishing
|
| + for (var i=0, l=names.length, n; i<l; i++) {
|
| + // remove excess ws
|
| + n = names[i].trim();
|
| + // looks weird, but causes n to exist on `publish` if it does not;
|
| + // a more careful test would need expensive `in` operator
|
| + if (n && publish[n] === undefined) {
|
| + publish[n] = undefined;
|
| + }
|
| + }
|
| + }
|
| + },
|
| +
|
| + // record clonable attributes from <element>
|
| + accumulateInstanceAttributes: function() {
|
| + // inherit instance attributes
|
| + var clonable = this.prototype._instanceAttributes;
|
| + // merge attributes from element
|
| + var a$ = this.attributes;
|
| + for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) {
|
| + if (this.isInstanceAttribute(a.name)) {
|
| + clonable[a.name] = a.value;
|
| + }
|
| + }
|
| + },
|
| +
|
| + isInstanceAttribute: function(name) {
|
| + return !this.blackList[name] && name.slice(0,3) !== 'on-';
|
| + },
|
| +
|
| + // do not clone these attributes onto instances
|
| + blackList: {
|
| + name: 1,
|
| + 'extends': 1,
|
| + constructor: 1,
|
| + noscript: 1,
|
| + assetpath: 1,
|
| + 'cache-csstext': 1
|
| + }
|
| +
|
| + };
|
| +
|
| + // add ATTRIBUTES_ATTRIBUTE to the blacklist
|
| + attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1;
|
| +
|
| + // exports
|
| +
|
| + scope.api.declaration.attributes = attributes;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| + var events = scope.api.declaration.events;
|
| +
|
| + var syntax = new PolymerExpressions();
|
| + var prepareBinding = syntax.prepareBinding;
|
| +
|
| + // Polymer takes a first crack at the binding to see if it's a declarative
|
| + // event handler.
|
| + syntax.prepareBinding = function(pathString, name, node) {
|
| + return events.prepareEventBinding(pathString, name, node) ||
|
| + prepareBinding.call(syntax, pathString, name, node);
|
| + };
|
| +
|
| + // declaration api supporting mdv
|
| + var mdv = {
|
| + syntax: syntax,
|
| + fetchTemplate: function() {
|
| + return this.querySelector('template');
|
| + },
|
| + templateContent: function() {
|
| + var template = this.fetchTemplate();
|
| + return template && template.content;
|
| + },
|
| + installBindingDelegate: function(template) {
|
| + if (template) {
|
| + template.bindingDelegate = this.syntax;
|
| + }
|
| + }
|
| + };
|
| +
|
| + // exports
|
| + scope.api.declaration.mdv = mdv;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var api = scope.api;
|
| + var isBase = scope.isBase;
|
| + var extend = scope.extend;
|
| +
|
| + var hasShadowDOMPolyfill = window.ShadowDOMPolyfill;
|
| +
|
| + // prototype api
|
| +
|
| + var prototype = {
|
| +
|
| + register: function(name, extendeeName) {
|
| + // build prototype combining extendee, Polymer base, and named api
|
| + this.buildPrototype(name, extendeeName);
|
| + // register our custom element with the platform
|
| + this.registerPrototype(name, extendeeName);
|
| + // reference constructor in a global named by 'constructor' attribute
|
| + this.publishConstructor();
|
| + },
|
| +
|
| + buildPrototype: function(name, extendeeName) {
|
| + // get our custom prototype (before chaining)
|
| + var extension = scope.getRegisteredPrototype(name);
|
| + // get basal prototype
|
| + var base = this.generateBasePrototype(extendeeName);
|
| + // implement declarative features
|
| + this.desugarBeforeChaining(extension, base);
|
| + // join prototypes
|
| + this.prototype = this.chainPrototypes(extension, base);
|
| + // more declarative features
|
| + this.desugarAfterChaining(name, extendeeName);
|
| + },
|
| +
|
| + desugarBeforeChaining: function(prototype, base) {
|
| + // back reference declaration element
|
| + // TODO(sjmiles): replace `element` with `elementElement` or `declaration`
|
| + prototype.element = this;
|
| + // transcribe `attributes` declarations onto own prototype's `publish`
|
| + this.publishAttributes(prototype, base);
|
| + // `publish` properties to the prototype and to attribute watch
|
| + this.publishProperties(prototype, base);
|
| + // infer observers for `observe` list based on method names
|
| + this.inferObservers(prototype);
|
| + // desugar compound observer syntax, e.g. 'a b c'
|
| + this.explodeObservers(prototype);
|
| + },
|
| +
|
| + chainPrototypes: function(prototype, base) {
|
| + // chain various meta-data objects to inherited versions
|
| + this.inheritMetaData(prototype, base);
|
| + // chain custom api to inherited
|
| + var chained = this.chainObject(prototype, base);
|
| + // x-platform fixup
|
| + ensurePrototypeTraversal(chained);
|
| + return chained;
|
| + },
|
| +
|
| + inheritMetaData: function(prototype, base) {
|
| + // chain observe object to inherited
|
| + this.inheritObject('observe', prototype, base);
|
| + // chain publish object to inherited
|
| + this.inheritObject('publish', prototype, base);
|
| + // chain reflect object to inherited
|
| + this.inheritObject('reflect', prototype, base);
|
| + // chain our lower-cased publish map to the inherited version
|
| + this.inheritObject('_publishLC', prototype, base);
|
| + // chain our instance attributes map to the inherited version
|
| + this.inheritObject('_instanceAttributes', prototype, base);
|
| + // chain our event delegates map to the inherited version
|
| + this.inheritObject('eventDelegates', prototype, base);
|
| + },
|
| +
|
| + // implement various declarative features
|
| + desugarAfterChaining: function(name, extendee) {
|
| + // build side-chained lists to optimize iterations
|
| + this.optimizePropertyMaps(this.prototype);
|
| + this.createPropertyAccessors(this.prototype);
|
| + // install mdv delegate on template
|
| + this.installBindingDelegate(this.fetchTemplate());
|
| + // install external stylesheets as if they are inline
|
| + this.installSheets();
|
| + // adjust any paths in dom from imports
|
| + this.resolveElementPaths(this);
|
| + // compile list of attributes to copy to instances
|
| + this.accumulateInstanceAttributes();
|
| + // parse on-* delegates declared on `this` element
|
| + this.parseHostEvents();
|
| + //
|
| + // install a helper method this.resolvePath to aid in
|
| + // setting resource urls. e.g.
|
| + // this.$.image.src = this.resolvePath('images/foo.png')
|
| + this.addResolvePathApi();
|
| + // under ShadowDOMPolyfill, transforms to approximate missing CSS features
|
| + if (hasShadowDOMPolyfill) {
|
| + WebComponents.ShadowCSS.shimStyling(this.templateContent(), name,
|
| + extendee);
|
| + }
|
| + // allow custom element access to the declarative context
|
| + if (this.prototype.registerCallback) {
|
| + this.prototype.registerCallback(this);
|
| + }
|
| + },
|
| +
|
| + // if a named constructor is requested in element, map a reference
|
| + // to the constructor to the given symbol
|
| + publishConstructor: function() {
|
| + var symbol = this.getAttribute('constructor');
|
| + if (symbol) {
|
| + window[symbol] = this.ctor;
|
| + }
|
| + },
|
| +
|
| + // build prototype combining extendee, Polymer base, and named api
|
| + generateBasePrototype: function(extnds) {
|
| + var prototype = this.findBasePrototype(extnds);
|
| + if (!prototype) {
|
| + // create a prototype based on tag-name extension
|
| + var prototype = HTMLElement.getPrototypeForTag(extnds);
|
| + // insert base api in inheritance chain (if needed)
|
| + prototype = this.ensureBaseApi(prototype);
|
| + // memoize this base
|
| + memoizedBases[extnds] = prototype;
|
| + }
|
| + return prototype;
|
| + },
|
| +
|
| + findBasePrototype: function(name) {
|
| + return memoizedBases[name];
|
| + },
|
| +
|
| + // install Polymer instance api into prototype chain, as needed
|
| + ensureBaseApi: function(prototype) {
|
| + if (prototype.PolymerBase) {
|
| + return prototype;
|
| + }
|
| + var extended = Object.create(prototype);
|
| + // we need a unique copy of base api for each base prototype
|
| + // therefore we 'extend' here instead of simply chaining
|
| + api.publish(api.instance, extended);
|
| + // TODO(sjmiles): sharing methods across prototype chains is
|
| + // not supported by 'super' implementation which optimizes
|
| + // by memoizing prototype relationships.
|
| + // Probably we should have a version of 'extend' that is
|
| + // share-aware: it could study the text of each function,
|
| + // look for usage of 'super', and wrap those functions in
|
| + // closures.
|
| + // As of now, there is only one problematic method, so
|
| + // we just patch it manually.
|
| + // To avoid re-entrancy problems, the special super method
|
| + // installed is called `mixinSuper` and the mixin method
|
| + // must use this method instead of the default `super`.
|
| + this.mixinMethod(extended, prototype, api.instance.mdv, 'bind');
|
| + // return buffed-up prototype
|
| + return extended;
|
| + },
|
| +
|
| + mixinMethod: function(extended, prototype, api, name) {
|
| + var $super = function(args) {
|
| + return prototype[name].apply(this, args);
|
| + };
|
| + extended[name] = function() {
|
| + this.mixinSuper = $super;
|
| + return api[name].apply(this, arguments);
|
| + }
|
| + },
|
| +
|
| + // ensure prototype[name] inherits from a prototype.prototype[name]
|
| + inheritObject: function(name, prototype, base) {
|
| + // require an object
|
| + var source = prototype[name] || {};
|
| + // chain inherited properties onto a new object
|
| + prototype[name] = this.chainObject(source, base[name]);
|
| + },
|
| +
|
| + // register 'prototype' to custom element 'name', store constructor
|
| + registerPrototype: function(name, extendee) {
|
| + var info = {
|
| + prototype: this.prototype
|
| + }
|
| + // native element must be specified in extends
|
| + var typeExtension = this.findTypeExtension(extendee);
|
| + if (typeExtension) {
|
| + info.extends = typeExtension;
|
| + }
|
| + // register the prototype with HTMLElement for name lookup
|
| + HTMLElement.register(name, this.prototype);
|
| + // register the custom type
|
| + this.ctor = document.registerElement(name, info);
|
| + },
|
| +
|
| + findTypeExtension: function(name) {
|
| + if (name && name.indexOf('-') < 0) {
|
| + return name;
|
| + } else {
|
| + var p = this.findBasePrototype(name);
|
| + if (p.element) {
|
| + return this.findTypeExtension(p.element.extends);
|
| + }
|
| + }
|
| + }
|
| +
|
| + };
|
| +
|
| + // memoize base prototypes
|
| + var memoizedBases = {};
|
| +
|
| + // implementation of 'chainObject' depends on support for __proto__
|
| + if (Object.__proto__) {
|
| + prototype.chainObject = function(object, inherited) {
|
| + if (object && inherited && object !== inherited) {
|
| + object.__proto__ = inherited;
|
| + }
|
| + return object;
|
| + }
|
| + } else {
|
| + prototype.chainObject = function(object, inherited) {
|
| + if (object && inherited && object !== inherited) {
|
| + var chained = Object.create(inherited);
|
| + object = extend(chained, object);
|
| + }
|
| + return object;
|
| + }
|
| + }
|
| +
|
| + // On platforms that do not support __proto__ (versions of IE), the prototype
|
| + // chain of a custom element is simulated via installation of __proto__.
|
| + // Although custom elements manages this, we install it here so it's
|
| + // available during desugaring.
|
| + function ensurePrototypeTraversal(prototype) {
|
| + if (!Object.__proto__) {
|
| + var ancestor = Object.getPrototypeOf(prototype);
|
| + prototype.__proto__ = ancestor;
|
| + if (isBase(ancestor)) {
|
| + ancestor.__proto__ = Object.getPrototypeOf(ancestor);
|
| + }
|
| + }
|
| + }
|
| +
|
| + // exports
|
| +
|
| + api.declaration.prototype = prototype;
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + /*
|
| +
|
| + Elements are added to a registration queue so that they register in
|
| + the proper order at the appropriate time. We do this for a few reasons:
|
| +
|
| + * to enable elements to load resources (like stylesheets)
|
| + asynchronously. We need to do this until the platform provides an efficient
|
| + alternative. One issue is that remote @import stylesheets are
|
| + re-fetched whenever stamped into a shadowRoot.
|
| +
|
| + * to ensure elements loaded 'at the same time' (e.g. via some set of
|
| + imports) are registered as a batch. This allows elements to be enured from
|
| + upgrade ordering as long as they query the dom tree 1 task after
|
| + upgrade (aka domReady). This is a performance tradeoff. On the one hand,
|
| + elements that could register while imports are loading are prevented from
|
| + doing so. On the other, grouping upgrades into a single task means less
|
| + incremental work (for example style recalcs), Also, we can ensure the
|
| + document is in a known state at the single quantum of time when
|
| + elements upgrade.
|
| +
|
| + */
|
| + var queue = {
|
| +
|
| + // tell the queue to wait for an element to be ready
|
| + wait: function(element) {
|
| + if (!element.__queue) {
|
| + element.__queue = {};
|
| + elements.push(element);
|
| + }
|
| + },
|
| +
|
| + // enqueue an element to the next spot in the queue.
|
| + enqueue: function(element, check, go) {
|
| + var shouldAdd = element.__queue && !element.__queue.check;
|
| + if (shouldAdd) {
|
| + queueForElement(element).push(element);
|
| + element.__queue.check = check;
|
| + element.__queue.go = go;
|
| + }
|
| + return (this.indexOf(element) !== 0);
|
| + },
|
| +
|
| + indexOf: function(element) {
|
| + var i = queueForElement(element).indexOf(element);
|
| + if (i >= 0 && document.contains(element)) {
|
| + i += (HTMLImports.useNative || HTMLImports.ready) ?
|
| + importQueue.length : 1e9;
|
| + }
|
| + return i;
|
| + },
|
| +
|
| + // tell the queue an element is ready to be registered
|
| + go: function(element) {
|
| + var readied = this.remove(element);
|
| + if (readied) {
|
| + element.__queue.flushable = true;
|
| + this.addToFlushQueue(readied);
|
| + this.check();
|
| + }
|
| + },
|
| +
|
| + remove: function(element) {
|
| + var i = this.indexOf(element);
|
| + if (i !== 0) {
|
| + //console.warn('queue order wrong', i);
|
| + return;
|
| + }
|
| + return queueForElement(element).shift();
|
| + },
|
| +
|
| + check: function() {
|
| + // next
|
| + var element = this.nextElement();
|
| + if (element) {
|
| + element.__queue.check.call(element);
|
| + }
|
| + if (this.canReady()) {
|
| + this.ready();
|
| + return true;
|
| + }
|
| + },
|
| +
|
| + nextElement: function() {
|
| + return nextQueued();
|
| + },
|
| +
|
| + canReady: function() {
|
| + return !this.waitToReady && this.isEmpty();
|
| + },
|
| +
|
| + isEmpty: function() {
|
| + for (var i=0, l=elements.length, e; (i<l) &&
|
| + (e=elements[i]); i++) {
|
| + if (e.__queue && !e.__queue.flushable) {
|
| + return;
|
| + }
|
| + }
|
| + return true;
|
| + },
|
| +
|
| + addToFlushQueue: function(element) {
|
| + flushQueue.push(element);
|
| + },
|
| +
|
| + flush: function() {
|
| + // prevent re-entrance
|
| + if (this.flushing) {
|
| + return;
|
| + }
|
| + this.flushing = true;
|
| + var element;
|
| + while (flushQueue.length) {
|
| + element = flushQueue.shift();
|
| + element.__queue.go.call(element);
|
| + element.__queue = null;
|
| + }
|
| + this.flushing = false;
|
| + },
|
| +
|
| + ready: function() {
|
| + // TODO(sorvell): As an optimization, turn off CE polyfill upgrading
|
| + // while registering. This way we avoid having to upgrade each document
|
| + // piecemeal per registration and can instead register all elements
|
| + // and upgrade once in a batch. Without this optimization, upgrade time
|
| + // degrades significantly when SD polyfill is used. This is mainly because
|
| + // querying the document tree for elements is slow under the SD polyfill.
|
| + var polyfillWasReady = CustomElements.ready;
|
| + CustomElements.ready = false;
|
| + this.flush();
|
| + if (!CustomElements.useNative) {
|
| + CustomElements.upgradeDocumentTree(document);
|
| + }
|
| + CustomElements.ready = polyfillWasReady;
|
| + Polymer.flush();
|
| + requestAnimationFrame(this.flushReadyCallbacks);
|
| + },
|
| +
|
| + addReadyCallback: function(callback) {
|
| + if (callback) {
|
| + readyCallbacks.push(callback);
|
| + }
|
| + },
|
| +
|
| + flushReadyCallbacks: function() {
|
| + if (readyCallbacks) {
|
| + var fn;
|
| + while (readyCallbacks.length) {
|
| + fn = readyCallbacks.shift();
|
| + fn();
|
| + }
|
| + }
|
| + },
|
| +
|
| + /**
|
| + Returns a list of elements that have had polymer-elements created but
|
| + are not yet ready to register. The list is an array of element definitions.
|
| + */
|
| + waitingFor: function() {
|
| + var e$ = [];
|
| + for (var i=0, l=elements.length, e; (i<l) &&
|
| + (e=elements[i]); i++) {
|
| + if (e.__queue && !e.__queue.flushable) {
|
| + e$.push(e);
|
| + }
|
| + }
|
| + return e$;
|
| + },
|
| +
|
| + waitToReady: true
|
| +
|
| + };
|
| +
|
| + var elements = [];
|
| + var flushQueue = [];
|
| + var importQueue = [];
|
| + var mainQueue = [];
|
| + var readyCallbacks = [];
|
| +
|
| + function queueForElement(element) {
|
| + return document.contains(element) ? mainQueue : importQueue;
|
| + }
|
| +
|
| + function nextQueued() {
|
| + return importQueue.length ? importQueue[0] : mainQueue[0];
|
| + }
|
| +
|
| + function whenReady(callback) {
|
| + queue.waitToReady = true;
|
| + Polymer.endOfMicrotask(function() {
|
| + HTMLImports.whenReady(function() {
|
| + queue.addReadyCallback(callback);
|
| + queue.waitToReady = false;
|
| + queue.check();
|
| + });
|
| + });
|
| + }
|
| +
|
| + /**
|
| + Forces polymer to register any pending elements. Can be used to abort
|
| + waiting for elements that are partially defined.
|
| + @param timeout {Integer} Optional timeout in milliseconds
|
| + */
|
| + function forceReady(timeout) {
|
| + if (timeout === undefined) {
|
| + queue.ready();
|
| + return;
|
| + }
|
| + var handle = setTimeout(function() {
|
| + queue.ready();
|
| + }, timeout);
|
| + Polymer.whenReady(function() {
|
| + clearTimeout(handle);
|
| + });
|
| + }
|
| +
|
| + // exports
|
| + scope.elements = elements;
|
| + scope.waitingFor = queue.waitingFor.bind(queue);
|
| + scope.forceReady = forceReady;
|
| + scope.queue = queue;
|
| + scope.whenReady = scope.whenPolymerReady = whenReady;
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| + // imports
|
| +
|
| + var extend = scope.extend;
|
| + var api = scope.api;
|
| + var queue = scope.queue;
|
| + var whenReady = scope.whenReady;
|
| + var getRegisteredPrototype = scope.getRegisteredPrototype;
|
| + var waitingForPrototype = scope.waitingForPrototype;
|
| +
|
| + // declarative implementation: <polymer-element>
|
| +
|
| + var prototype = extend(Object.create(HTMLElement.prototype), {
|
| +
|
| + createdCallback: function() {
|
| + if (this.getAttribute('name')) {
|
| + this.init();
|
| + }
|
| + },
|
| +
|
| + init: function() {
|
| + // fetch declared values
|
| + this.name = this.getAttribute('name');
|
| + this.extends = this.getAttribute('extends');
|
| + queue.wait(this);
|
| + // initiate any async resource fetches
|
| + this.loadResources();
|
| + // register when all constraints are met
|
| + this.registerWhenReady();
|
| + },
|
| +
|
| + // TODO(sorvell): we currently queue in the order the prototypes are
|
| + // registered, but we should queue in the order that polymer-elements
|
| + // are registered. We are currently blocked from doing this based on
|
| + // crbug.com/395686.
|
| + registerWhenReady: function() {
|
| + if (this.registered
|
| + || this.waitingForPrototype(this.name)
|
| + || this.waitingForQueue()
|
| + || this.waitingForResources()) {
|
| + return;
|
| + }
|
| + queue.go(this);
|
| + },
|
| +
|
| + _register: function() {
|
| + //console.log('registering', this.name);
|
| + // warn if extending from a custom element not registered via Polymer
|
| + if (isCustomTag(this.extends) && !isRegistered(this.extends)) {
|
| + console.warn('%s is attempting to extend %s, an unregistered element ' +
|
| + 'or one that was not registered with Polymer.', this.name,
|
| + this.extends);
|
| + }
|
| + this.register(this.name, this.extends);
|
| + this.registered = true;
|
| + },
|
| +
|
| + waitingForPrototype: function(name) {
|
| + if (!getRegisteredPrototype(name)) {
|
| + // then wait for a prototype
|
| + waitingForPrototype(name, this);
|
| + // emulate script if user is not supplying one
|
| + this.handleNoScript(name);
|
| + // prototype not ready yet
|
| + return true;
|
| + }
|
| + },
|
| +
|
| + handleNoScript: function(name) {
|
| + // if explicitly marked as 'noscript'
|
| + if (this.hasAttribute('noscript') && !this.noscript) {
|
| + this.noscript = true;
|
| + // imperative element registration
|
| + Polymer(name);
|
| + }
|
| + },
|
| +
|
| + waitingForResources: function() {
|
| + return this._needsResources;
|
| + },
|
| +
|
| + // NOTE: Elements must be queued in proper order for inheritance/composition
|
| + // dependency resolution. Previously this was enforced for inheritance,
|
| + // and by rule for composition. It's now entirely by rule.
|
| + waitingForQueue: function() {
|
| + return queue.enqueue(this, this.registerWhenReady, this._register);
|
| + },
|
| +
|
| + loadResources: function() {
|
| + this._needsResources = true;
|
| + this.loadStyles(function() {
|
| + this._needsResources = false;
|
| + this.registerWhenReady();
|
| + }.bind(this));
|
| + }
|
| +
|
| + });
|
| +
|
| + // semi-pluggable APIs
|
| +
|
| + // TODO(sjmiles): should be fully pluggable (aka decoupled, currently
|
| + // the various plugins are allowed to depend on each other directly)
|
| + api.publish(api.declaration, prototype);
|
| +
|
| + // utility and bookkeeping
|
| +
|
| + function isRegistered(name) {
|
| + return Boolean(HTMLElement.getPrototypeForTag(name));
|
| + }
|
| +
|
| + function isCustomTag(name) {
|
| + return (name && name.indexOf('-') >= 0);
|
| + }
|
| +
|
| + // boot tasks
|
| +
|
| + whenReady(function() {
|
| + document.body.removeAttribute('unresolved');
|
| + document.dispatchEvent(
|
| + new CustomEvent('polymer-ready', {bubbles: true})
|
| + );
|
| + });
|
| +
|
| + // register polymer-element with document
|
| +
|
| + document.registerElement('polymer-element', {prototype: prototype});
|
| +
|
| +})(Polymer);
|
| +
|
| +(function(scope) {
|
| +
|
| +/**
|
| + * @class Polymer
|
| + */
|
| +
|
| +var whenReady = scope.whenReady;
|
| +
|
| +/**
|
| + * Loads the set of HTMLImports contained in `node`. Notifies when all
|
| + * the imports have loaded by calling the `callback` function argument.
|
| + * This method can be used to lazily load imports. For example, given a
|
| + * template:
|
| + *
|
| + * <template>
|
| + * <link rel="import" href="my-import1.html">
|
| + * <link rel="import" href="my-import2.html">
|
| + * </template>
|
| + *
|
| + * Polymer.importElements(template.content, function() {
|
| + * console.log('imports lazily loaded');
|
| + * });
|
| + *
|
| + * @method importElements
|
| + * @param {Node} node Node containing the HTMLImports to load.
|
| + * @param {Function} callback Callback called when all imports have loaded.
|
| + */
|
| +function importElements(node, callback) {
|
| + if (node) {
|
| + document.head.appendChild(node);
|
| + whenReady(callback);
|
| + } else if (callback) {
|
| + callback();
|
| + }
|
| +}
|
| +
|
| +/**
|
| + * Loads an HTMLImport for each url specified in the `urls` array.
|
| + * Notifies when all the imports have loaded by calling the `callback`
|
| + * function argument. This method can be used to lazily load imports.
|
| + * For example,
|
| + *
|
| + * Polymer.import(['my-import1.html', 'my-import2.html'], function() {
|
| + * console.log('imports lazily loaded');
|
| + * });
|
| + *
|
| + * @method import
|
| + * @param {Array} urls Array of urls to load as HTMLImports.
|
| + * @param {Function} callback Callback called when all imports have loaded.
|
| + */
|
| +function _import(urls, callback) {
|
| + if (urls && urls.length) {
|
| + var frag = document.createDocumentFragment();
|
| + for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) {
|
| + link = document.createElement('link');
|
| + link.rel = 'import';
|
| + link.href = url;
|
| + frag.appendChild(link);
|
| + }
|
| + importElements(frag, callback);
|
| + } else if (callback) {
|
| + callback();
|
| + }
|
| +}
|
| +
|
| +// exports
|
| +scope.import = _import;
|
| +scope.importElements = importElements;
|
| +
|
| +})(Polymer);
|
| +
|
| +/**
|
| + * The `auto-binding` element extends the template element. It provides a quick
|
| + * and easy way to do data binding without the need to setup a model.
|
| + * The `auto-binding` element itself serves as the model and controller for the
|
| + * elements it contains. Both data and event handlers can be bound.
|
| + *
|
| + * The `auto-binding` element acts just like a template that is bound to
|
| + * a model. It stamps its content in the dom adjacent to itself. When the
|
| + * content is stamped, the `template-bound` event is fired.
|
| + *
|
| + * Example:
|
| + *
|
| + * <template is="auto-binding">
|
| + * <div>Say something: <input value="{{value}}"></div>
|
| + * <div>You said: {{value}}</div>
|
| + * <button on-tap="{{buttonTap}}">Tap me!</button>
|
| + * </template>
|
| + * <script>
|
| + * var template = document.querySelector('template');
|
| + * template.value = 'something';
|
| + * template.buttonTap = function() {
|
| + * console.log('tap!');
|
| + * };
|
| + * </script>
|
| + *
|
| + * @module Polymer
|
| + * @status stable
|
| +*/
|
| +
|
| +(function() {
|
| +
|
| + var element = document.createElement('polymer-element');
|
| + element.setAttribute('name', 'auto-binding');
|
| + element.setAttribute('extends', 'template');
|
| + element.init();
|
| +
|
| + Polymer('auto-binding', {
|
| +
|
| + createdCallback: function() {
|
| + this.syntax = this.bindingDelegate = this.makeSyntax();
|
| + // delay stamping until polymer-ready so that auto-binding is not
|
| + // required to load last.
|
| + Polymer.whenPolymerReady(function() {
|
| + this.model = this;
|
| + this.setAttribute('bind', '');
|
| + // we don't bother with an explicit signal here, we could ust a MO
|
| + // if necessary
|
| + this.async(function() {
|
| + // note: this will marshall *all* the elements in the parentNode
|
| + // rather than just stamped ones. We'd need to use createInstance
|
| + // to fix this or something else fancier.
|
| + this.marshalNodeReferences(this.parentNode);
|
| + // template stamping is asynchronous so stamping isn't complete
|
| + // by polymer-ready; fire an event so users can use stamped elements
|
| + this.fire('template-bound');
|
| + });
|
| + }.bind(this));
|
| + },
|
| +
|
| + makeSyntax: function() {
|
| + var events = Object.create(Polymer.api.declaration.events);
|
| + var self = this;
|
| + events.findController = function() { return self.model; };
|
| +
|
| + var syntax = new PolymerExpressions();
|
| + var prepareBinding = syntax.prepareBinding;
|
| + syntax.prepareBinding = function(pathString, name, node) {
|
| + return events.prepareEventBinding(pathString, name, node) ||
|
| + prepareBinding.call(syntax, pathString, name, node);
|
| + };
|
| + return syntax;
|
| + }
|
| +
|
| + });
|
| +
|
| +})();
|
|
|