| Index: runtime/bin/vmservice/observatory/deployed/web/packages/template_binding/js/node_bind.js
|
| diff --git a/runtime/bin/vmservice/observatory/deployed/web/packages/template_binding/js/node_bind.js b/runtime/bin/vmservice/observatory/deployed/web/packages/template_binding/js/node_bind.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..270c557b185ea8a36c5cabb97affeb865322e483
|
| --- /dev/null
|
| +++ b/runtime/bin/vmservice/observatory/deployed/web/packages/template_binding/js/node_bind.js
|
| @@ -0,0 +1,343 @@
|
| +// 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);
|
|
|