OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 3 * Use of this source code is governed by a BSD-style |
| 4 * license that can be found in the LICENSE file. |
| 5 */ |
| 6 Polymer = {}; |
| 7 |
| 8 /* |
| 9 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 10 * Use of this source code is governed by a BSD-style |
| 11 * license that can be found in the LICENSE file. |
| 12 */ |
| 13 |
| 14 // TODO(sorvell): this ensures Polymer is an object and not a function |
| 15 // Platform is currently defining it as a function to allow for async loading |
| 16 // of polymer; once we refine the loading process this likely goes away. |
| 17 if (typeof window.Polymer === 'function') { |
| 18 Polymer = {}; |
| 19 } |
| 20 |
| 21 |
| 22 /* |
| 23 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 24 * Use of this source code is governed by a BSD-style |
| 25 * license that can be found in the LICENSE file. |
| 26 */ |
| 27 (function(scope) { |
| 28 |
| 29 // copy own properties from 'api' to 'prototype, with name hinting for 'super' |
| 30 function extend(prototype, api) { |
| 31 if (prototype && api) { |
| 32 // use only own properties of 'api' |
| 33 Object.getOwnPropertyNames(api).forEach(function(n) { |
| 34 // acquire property descriptor |
| 35 var pd = Object.getOwnPropertyDescriptor(api, n); |
| 36 if (pd) { |
| 37 // clone property via descriptor |
| 38 Object.defineProperty(prototype, n, pd); |
| 39 // cache name-of-method for 'super' engine |
| 40 if (typeof pd.value == 'function') { |
| 41 // hint the 'super' engine |
| 42 pd.value.nom = n; |
| 43 } |
| 44 } |
| 45 }); |
| 46 } |
| 47 return prototype; |
| 48 } |
| 49 |
| 50 // exports |
| 51 |
| 52 scope.extend = extend; |
| 53 |
| 54 })(Polymer); |
| 55 |
| 56 /* |
| 57 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 58 * Use of this source code is governed by a BSD-style |
| 59 * license that can be found in the LICENSE file. |
| 60 */ |
| 61 |
| 62 (function(scope) { |
| 63 |
| 64 // usage |
| 65 |
| 66 // invoke cb.call(this) in 100ms, unless the job is re-registered, |
| 67 // which resets the timer |
| 68 // |
| 69 // this.myJob = this.job(this.myJob, cb, 100) |
| 70 // |
| 71 // returns a job handle which can be used to re-register a job |
| 72 |
| 73 var Job = function(inContext) { |
| 74 this.context = inContext; |
| 75 this.boundComplete = this.complete.bind(this) |
| 76 }; |
| 77 Job.prototype = { |
| 78 go: function(callback, wait) { |
| 79 this.callback = callback; |
| 80 var h; |
| 81 if (!wait) { |
| 82 h = requestAnimationFrame(this.boundComplete); |
| 83 this.handle = function() { |
| 84 cancelAnimationFrame(h); |
| 85 } |
| 86 } else { |
| 87 h = setTimeout(this.boundComplete, wait); |
| 88 this.handle = function() { |
| 89 clearTimeout(h); |
| 90 } |
| 91 } |
| 92 }, |
| 93 stop: function() { |
| 94 if (this.handle) { |
| 95 this.handle(); |
| 96 this.handle = null; |
| 97 } |
| 98 }, |
| 99 complete: function() { |
| 100 if (this.handle) { |
| 101 this.stop(); |
| 102 this.callback.call(this.context); |
| 103 } |
| 104 } |
| 105 }; |
| 106 |
| 107 function job(job, callback, wait) { |
| 108 if (job) { |
| 109 job.stop(); |
| 110 } else { |
| 111 job = new Job(this); |
| 112 } |
| 113 job.go(callback, wait); |
| 114 return job; |
| 115 } |
| 116 |
| 117 // exports |
| 118 |
| 119 scope.job = job; |
| 120 |
| 121 })(Polymer); |
| 122 |
| 123 /* |
| 124 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 125 * Use of this source code is governed by a BSD-style |
| 126 * license that can be found in the LICENSE file. |
| 127 */ |
| 128 (function(scope) { |
| 129 |
| 130 var registry = {}; |
| 131 |
| 132 HTMLElement.register = function(tag, prototype) { |
| 133 registry[tag] = prototype; |
| 134 } |
| 135 |
| 136 // get prototype mapped to node <tag> |
| 137 HTMLElement.getPrototypeForTag = function(tag) { |
| 138 var prototype = !tag ? HTMLElement.prototype : registry[tag]; |
| 139 // TODO(sjmiles): creating <tag> is likely to have wasteful side-effects |
| 140 return prototype || Object.getPrototypeOf(document.createElement(tag)); |
| 141 }; |
| 142 |
| 143 // we have to flag propagation stoppage for the event dispatcher |
| 144 var originalStopPropagation = Event.prototype.stopPropagation; |
| 145 Event.prototype.stopPropagation = function() { |
| 146 this.cancelBubble = true; |
| 147 originalStopPropagation.apply(this, arguments); |
| 148 }; |
| 149 |
| 150 // TODO(sorvell): remove when we're sure imports does not need |
| 151 // to load stylesheets |
| 152 /* |
| 153 HTMLImports.importer.preloadSelectors += |
| 154 ', polymer-element link[rel=stylesheet]'; |
| 155 */ |
| 156 })(Polymer); |
| 157 |
| 158 /* |
| 159 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 160 * Use of this source code is governed by a BSD-style |
| 161 * license that can be found in the LICENSE file. |
| 162 */ |
| 163 (function(scope) { |
| 164 // super |
| 165 |
| 166 // `arrayOfArgs` is an optional array of args like one might pass |
| 167 // to `Function.apply` |
| 168 |
| 169 // TODO(sjmiles): |
| 170 // $super must be installed on an instance or prototype chain |
| 171 // as `super`, and invoked via `this`, e.g. |
| 172 // `this.super();` |
| 173 |
| 174 // will not work if function objects are not unique, for example, |
| 175 // when using mixins. |
| 176 // The memoization strategy assumes each function exists on only one |
| 177 // prototype chain i.e. we use the function object for memoizing) |
| 178 // perhaps we can bookkeep on the prototype itself instead |
| 179 function $super(arrayOfArgs) { |
| 180 // since we are thunking a method call, performance is important here: |
| 181 // memoize all lookups, once memoized the fast path calls no other |
| 182 // functions |
| 183 // |
| 184 // find the caller (cannot be `strict` because of 'caller') |
| 185 var caller = $super.caller; |
| 186 // memoized 'name of method' |
| 187 var nom = caller.nom; |
| 188 // memoized next implementation prototype |
| 189 var _super = caller._super; |
| 190 if (!_super) { |
| 191 if (!nom) { |
| 192 nom = caller.nom = nameInThis.call(this, caller); |
| 193 } |
| 194 if (!nom) { |
| 195 console.warn('called super() on a method not installed declaratively (
has no .nom property)'); |
| 196 } |
| 197 // super prototype is either cached or we have to find it |
| 198 // by searching __proto__ (at the 'top') |
| 199 _super = memoizeSuper(caller, nom, getPrototypeOf(this)); |
| 200 } |
| 201 if (!_super) { |
| 202 // if _super is falsey, there is no super implementation |
| 203 //console.warn('called $super(' + nom + ') where there is no super imple
mentation'); |
| 204 } else { |
| 205 // our super function |
| 206 var fn = _super[nom]; |
| 207 // memoize information so 'fn' can call 'super' |
| 208 if (!fn._super) { |
| 209 memoizeSuper(fn, nom, _super); |
| 210 } |
| 211 // invoke the inherited method |
| 212 // if 'fn' is not function valued, this will throw |
| 213 return fn.apply(this, arrayOfArgs || []); |
| 214 } |
| 215 } |
| 216 |
| 217 function nextSuper(proto, name, caller) { |
| 218 // look for an inherited prototype that implements name |
| 219 while (proto) { |
| 220 if ((proto[name] !== caller) && proto[name]) { |
| 221 return proto; |
| 222 } |
| 223 proto = getPrototypeOf(proto); |
| 224 } |
| 225 } |
| 226 |
| 227 function memoizeSuper(method, name, proto) { |
| 228 // find and cache next prototype containing `name` |
| 229 // we need the prototype so we can do another lookup |
| 230 // from here |
| 231 method._super = nextSuper(proto, name, method); |
| 232 if (method._super) { |
| 233 // _super is a prototype, the actual method is _super[name] |
| 234 // tag super method with it's name for further lookups |
| 235 method._super[name].nom = name; |
| 236 } |
| 237 return method._super; |
| 238 } |
| 239 |
| 240 function nameInThis(value) { |
| 241 var p = this.__proto__; |
| 242 while (p && p !== HTMLElement.prototype) { |
| 243 // TODO(sjmiles): getOwnPropertyNames is absurdly expensive |
| 244 var n$ = Object.getOwnPropertyNames(p); |
| 245 for (var i=0, l=n$.length, n; i<l && (n=n$[i]); i++) { |
| 246 var d = Object.getOwnPropertyDescriptor(p, n); |
| 247 if (typeof d.value === 'function' && d.value === value) { |
| 248 return n; |
| 249 } |
| 250 } |
| 251 p = p.__proto__; |
| 252 } |
| 253 } |
| 254 |
| 255 // NOTE: In some platforms (IE10) the prototype chain is faked via |
| 256 // __proto__. Therefore, always get prototype via __proto__ instead of |
| 257 // the more standard Object.getPrototypeOf. |
| 258 function getPrototypeOf(prototype) { |
| 259 return prototype.__proto__; |
| 260 } |
| 261 |
| 262 // utility function to precompute name tags for functions |
| 263 // in a (unchained) prototype |
| 264 function hintSuper(prototype) { |
| 265 // tag functions with their prototype name to optimize |
| 266 // super call invocations |
| 267 for (var n in prototype) { |
| 268 var pd = Object.getOwnPropertyDescriptor(prototype, n); |
| 269 if (pd && typeof pd.value === 'function') { |
| 270 pd.value.nom = n; |
| 271 } |
| 272 } |
| 273 } |
| 274 |
| 275 // exports |
| 276 |
| 277 scope.super = $super; |
| 278 |
| 279 })(Polymer); |
| 280 |
| 281 /* |
| 282 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 283 * Use of this source code is governed by a BSD-style |
| 284 * license that can be found in the LICENSE file. |
| 285 */ |
| 286 |
| 287 (function(scope) { |
| 288 |
| 289 var typeHandlers = { |
| 290 string: function(value) { |
| 291 return value; |
| 292 }, |
| 293 date: function(value) { |
| 294 return new Date(Date.parse(value) || Date.now()); |
| 295 }, |
| 296 boolean: function(value) { |
| 297 if (value === '') { |
| 298 return true; |
| 299 } |
| 300 return value === 'false' ? false : !!value; |
| 301 }, |
| 302 number: function(value) { |
| 303 var n = parseFloat(value); |
| 304 // hex values like "0xFFFF" parseFloat as 0 |
| 305 if (n === 0) { |
| 306 n = parseInt(value); |
| 307 } |
| 308 return isNaN(n) ? value : n; |
| 309 // this code disabled because encoded values (like "0xFFFF") |
| 310 // do not round trip to their original format |
| 311 //return (String(floatVal) === value) ? floatVal : value; |
| 312 }, |
| 313 object: function(value, currentValue) { |
| 314 if (currentValue === null) { |
| 315 return value; |
| 316 } |
| 317 try { |
| 318 // If the string is an object, we can parse is with the JSON library. |
| 319 // include convenience replace for single-quotes. If the author omits |
| 320 // quotes altogether, parse will fail. |
| 321 return JSON.parse(value.replace(/'/g, '"')); |
| 322 } catch(e) { |
| 323 // The object isn't valid JSON, return the raw value |
| 324 return value; |
| 325 } |
| 326 }, |
| 327 // avoid deserialization of functions |
| 328 'function': function(value, currentValue) { |
| 329 return currentValue; |
| 330 } |
| 331 }; |
| 332 |
| 333 function deserializeValue(value, currentValue) { |
| 334 // attempt to infer type from default value |
| 335 var inferredType = typeof currentValue; |
| 336 // invent 'date' type value for Date |
| 337 if (currentValue instanceof Date) { |
| 338 inferredType = 'date'; |
| 339 } |
| 340 // delegate deserialization via type string |
| 341 return typeHandlers[inferredType](value, currentValue); |
| 342 } |
| 343 |
| 344 // exports |
| 345 |
| 346 scope.deserializeValue = deserializeValue; |
| 347 |
| 348 })(Polymer); |
| 349 |
| 350 /* |
| 351 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 352 * Use of this source code is governed by a BSD-style |
| 353 * license that can be found in the LICENSE file. |
| 354 */ |
| 355 (function(scope) { |
| 356 |
| 357 // imports |
| 358 |
| 359 var extend = scope.extend; |
| 360 |
| 361 // module |
| 362 |
| 363 var api = {}; |
| 364 |
| 365 api.declaration = {}; |
| 366 api.instance = {}; |
| 367 |
| 368 api.publish = function(apis, prototype) { |
| 369 for (var n in apis) { |
| 370 extend(prototype, apis[n]); |
| 371 } |
| 372 } |
| 373 |
| 374 // exports |
| 375 |
| 376 scope.api = api; |
| 377 |
| 378 })(Polymer); |
| 379 |
| 380 /* |
| 381 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 382 * Use of this source code is governed by a BSD-style |
| 383 * license that can be found in the LICENSE file. |
| 384 */ |
| 385 (function(scope) { |
| 386 |
| 387 var utils = { |
| 388 /** |
| 389 * Invokes a function asynchronously. The context of the callback |
| 390 * function is bound to 'this' automatically. |
| 391 * @method async |
| 392 * @param {Function|String} method |
| 393 * @param {any|Array} args |
| 394 * @param {number} timeout |
| 395 */ |
| 396 async: function(method, args, timeout) { |
| 397 // when polyfilling Object.observe, ensure changes |
| 398 // propagate before executing the async method |
| 399 Platform.flush(); |
| 400 // second argument to `apply` must be an array |
| 401 args = (args && args.length) ? args : [args]; |
| 402 // function to invoke |
| 403 var fn = function() { |
| 404 (this[method] || method).apply(this, args); |
| 405 }.bind(this); |
| 406 // execute `fn` sooner or later |
| 407 var handle = timeout ? setTimeout(fn, timeout) : |
| 408 requestAnimationFrame(fn); |
| 409 // NOTE: switch on inverting handle to determine which time is used. |
| 410 return timeout ? handle : 1 / handle; |
| 411 }, |
| 412 cancelAsync: function(handle) { |
| 413 if (handle < 1) { |
| 414 cancelAnimationFrame(Math.round(1 / handle)); |
| 415 } else { |
| 416 clearTimeout(handle); |
| 417 } |
| 418 }, |
| 419 /** |
| 420 * Fire an event. |
| 421 * @method fire |
| 422 * @returns {Object} event |
| 423 * @param {string} type An event name. |
| 424 * @param {any} detail |
| 425 * @param {Node} onNode Target node. |
| 426 */ |
| 427 fire: function(type, detail, onNode, bubbles, cancelable) { |
| 428 var node = onNode || this; |
| 429 var detail = detail || {}; |
| 430 var event = new CustomEvent(type, { |
| 431 bubbles: (bubbles !== undefined ? bubbles : true), |
| 432 cancelable: (cancelable !== undefined ? cancelable : true), |
| 433 detail: detail |
| 434 }); |
| 435 node.dispatchEvent(event); |
| 436 return event; |
| 437 }, |
| 438 /** |
| 439 * Fire an event asynchronously. |
| 440 * @method asyncFire |
| 441 * @param {string} type An event name. |
| 442 * @param detail |
| 443 * @param {Node} toNode Target node. |
| 444 */ |
| 445 asyncFire: function(/*inType, inDetail*/) { |
| 446 this.async("fire", arguments); |
| 447 }, |
| 448 /** |
| 449 * Remove class from old, add class to anew, if they exist |
| 450 * @param classFollows |
| 451 * @param anew A node. |
| 452 * @param old A node |
| 453 * @param className |
| 454 */ |
| 455 classFollows: function(anew, old, className) { |
| 456 if (old) { |
| 457 old.classList.remove(className); |
| 458 } |
| 459 if (anew) { |
| 460 anew.classList.add(className); |
| 461 } |
| 462 } |
| 463 }; |
| 464 |
| 465 // no-operation function for handy stubs |
| 466 var nop = function() {}; |
| 467 |
| 468 // null-object for handy stubs |
| 469 var nob = {}; |
| 470 |
| 471 // deprecated |
| 472 |
| 473 utils.asyncMethod = utils.async; |
| 474 |
| 475 // exports |
| 476 |
| 477 scope.api.instance.utils = utils; |
| 478 scope.nop = nop; |
| 479 scope.nob = nob; |
| 480 |
| 481 })(Polymer); |
| 482 |
| 483 /* |
| 484 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 485 * Use of this source code is governed by a BSD-style |
| 486 * license that can be found in the LICENSE file. |
| 487 */ |
| 488 |
| 489 (function(scope) { |
| 490 |
| 491 // imports |
| 492 |
| 493 var log = window.logFlags || {}; |
| 494 var EVENT_PREFIX = 'on-'; |
| 495 |
| 496 // instance events api |
| 497 var events = { |
| 498 // read-only |
| 499 EVENT_PREFIX: EVENT_PREFIX, |
| 500 // event listeners on host |
| 501 addHostListeners: function() { |
| 502 var events = this.eventDelegates; |
| 503 log.events && (Object.keys(events).length > 0) && console.log('[%s] addHos
tListeners:', this.localName, events); |
| 504 // NOTE: host events look like bindings but really are not; |
| 505 // (1) we don't want the attribute to be set and (2) we want to support |
| 506 // multiple event listeners ('host' and 'instance') and Node.bind |
| 507 // by default supports 1 thing being bound. |
| 508 // We do, however, leverage the event hookup code in PolymerExpressions |
| 509 // so that we have a common code path for handling declarative events. |
| 510 var self = this, bindable, eventName; |
| 511 for (var n in events) { |
| 512 eventName = EVENT_PREFIX + n; |
| 513 bindable = PolymerExpressions.prepareEventBinding( |
| 514 Path.get(events[n]), |
| 515 eventName, |
| 516 { |
| 517 resolveEventHandler: function(model, path, node) { |
| 518 var fn = path.getValueFrom(self); |
| 519 if (fn) { |
| 520 return fn.bind(self); |
| 521 } |
| 522 } |
| 523 } |
| 524 ); |
| 525 bindable(this, this, false); |
| 526 } |
| 527 }, |
| 528 // call 'method' or function method on 'obj' with 'args', if the method exis
ts |
| 529 dispatchMethod: function(obj, method, args) { |
| 530 if (obj) { |
| 531 log.events && console.group('[%s] dispatch [%s]', obj.localName, method)
; |
| 532 var fn = typeof method === 'function' ? method : obj[method]; |
| 533 if (fn) { |
| 534 fn[args ? 'apply' : 'call'](obj, args); |
| 535 } |
| 536 log.events && console.groupEnd(); |
| 537 Platform.flush(); |
| 538 } |
| 539 } |
| 540 }; |
| 541 |
| 542 // exports |
| 543 |
| 544 scope.api.instance.events = events; |
| 545 |
| 546 })(Polymer); |
| 547 |
| 548 /* |
| 549 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 550 * Use of this source code is governed by a BSD-style |
| 551 * license that can be found in the LICENSE file. |
| 552 */ |
| 553 (function(scope) { |
| 554 |
| 555 // instance api for attributes |
| 556 |
| 557 var attributes = { |
| 558 copyInstanceAttributes: function () { |
| 559 var a$ = this._instanceAttributes; |
| 560 for (var k in a$) { |
| 561 if (!this.hasAttribute(k)) { |
| 562 this.setAttribute(k, a$[k]); |
| 563 } |
| 564 } |
| 565 }, |
| 566 // for each attribute on this, deserialize value to property as needed |
| 567 takeAttributes: function() { |
| 568 // if we have no publish lookup table, we have no attributes to take |
| 569 // TODO(sjmiles): ad hoc |
| 570 if (this._publishLC) { |
| 571 for (var i=0, a$=this.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++)
{ |
| 572 this.attributeToProperty(a.name, a.value); |
| 573 } |
| 574 } |
| 575 }, |
| 576 // if attribute 'name' is mapped to a property, deserialize |
| 577 // 'value' into that property |
| 578 attributeToProperty: function(name, value) { |
| 579 // try to match this attribute to a property (attributes are |
| 580 // all lower-case, so this is case-insensitive search) |
| 581 var name = this.propertyForAttribute(name); |
| 582 if (name) { |
| 583 // filter out 'mustached' values, these are to be |
| 584 // replaced with bound-data and are not yet values |
| 585 // themselves |
| 586 if (value && value.search(scope.bindPattern) >= 0) { |
| 587 return; |
| 588 } |
| 589 // get original value |
| 590 var currentValue = this[name]; |
| 591 // deserialize Boolean or Number values from attribute |
| 592 var value = this.deserializeValue(value, currentValue); |
| 593 // only act if the value has changed |
| 594 if (value !== currentValue) { |
| 595 // install new value (has side-effects) |
| 596 this[name] = value; |
| 597 } |
| 598 } |
| 599 }, |
| 600 // return the published property matching name, or undefined |
| 601 propertyForAttribute: function(name) { |
| 602 var match = this._publishLC && this._publishLC[name]; |
| 603 //console.log('propertyForAttribute:', name, 'matches', match); |
| 604 return match; |
| 605 }, |
| 606 // convert representation of 'stringValue' based on type of 'currentValue' |
| 607 deserializeValue: function(stringValue, currentValue) { |
| 608 return scope.deserializeValue(stringValue, currentValue); |
| 609 }, |
| 610 serializeValue: function(value, inferredType) { |
| 611 if (inferredType === 'boolean') { |
| 612 return value ? '' : undefined; |
| 613 } else if (inferredType !== 'object' && inferredType !== 'function' |
| 614 && value !== undefined) { |
| 615 return value; |
| 616 } |
| 617 }, |
| 618 reflectPropertyToAttribute: function(name) { |
| 619 var inferredType = typeof this[name]; |
| 620 // try to intelligently serialize property value |
| 621 var serializedValue = this.serializeValue(this[name], inferredType); |
| 622 // boolean properties must reflect as boolean attributes |
| 623 if (serializedValue !== undefined) { |
| 624 this.setAttribute(name, serializedValue); |
| 625 // TODO(sorvell): we should remove attr for all properties |
| 626 // that have undefined serialization; however, we will need to |
| 627 // refine the attr reflection system to achieve this; pica, for example, |
| 628 // relies on having inferredType object properties not removed as |
| 629 // attrs. |
| 630 } else if (inferredType === 'boolean') { |
| 631 this.removeAttribute(name); |
| 632 } |
| 633 } |
| 634 }; |
| 635 |
| 636 // exports |
| 637 |
| 638 scope.api.instance.attributes = attributes; |
| 639 |
| 640 })(Polymer); |
| 641 |
| 642 /* |
| 643 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 644 * Use of this source code is governed by a BSD-style |
| 645 * license that can be found in the LICENSE file. |
| 646 */ |
| 647 (function(scope) { |
| 648 |
| 649 // imports |
| 650 |
| 651 var log = window.logFlags || {}; |
| 652 |
| 653 // magic words |
| 654 |
| 655 var OBSERVE_SUFFIX = 'Changed'; |
| 656 |
| 657 // element api |
| 658 |
| 659 var empty = []; |
| 660 |
| 661 var properties = { |
| 662 observeProperties: function() { |
| 663 var n$ = this._observeNames, pn$ = this._publishNames; |
| 664 if ((n$ && n$.length) || (pn$ && pn$.length)) { |
| 665 var self = this; |
| 666 var o = this._propertyObserver = new CompoundObserver(); |
| 667 for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) { |
| 668 o.addPath(this, n); |
| 669 // observer array properties |
| 670 var pd = Object.getOwnPropertyDescriptor(this.__proto__, n); |
| 671 if (pd && pd.value) { |
| 672 this.observeArrayValue(n, pd.value, null); |
| 673 } |
| 674 } |
| 675 for (var i=0, l=pn$.length, n; (i<l) && (n=pn$[i]); i++) { |
| 676 if (!this.observe || (this.observe[n] === undefined)) { |
| 677 o.addPath(this, n); |
| 678 } |
| 679 } |
| 680 o.open(this.notifyPropertyChanges, this); |
| 681 } |
| 682 }, |
| 683 notifyPropertyChanges: function(newValues, oldValues, paths) { |
| 684 var name, method, called = {}; |
| 685 for (var i in oldValues) { |
| 686 // note: paths is of form [object, path, object, path] |
| 687 name = paths[2 * i + 1]; |
| 688 if (this.publish[name] !== undefined) { |
| 689 this.reflectPropertyToAttribute(name); |
| 690 } |
| 691 method = this.observe[name]; |
| 692 if (method) { |
| 693 this.observeArrayValue(name, newValues[i], oldValues[i]); |
| 694 if (!called[method]) { |
| 695 called[method] = true; |
| 696 // observes the value if it is an array |
| 697 this.invokeMethod(method, [oldValues[i], newValues[i], arguments]); |
| 698 } |
| 699 } |
| 700 } |
| 701 }, |
| 702 observeArrayValue: function(name, value, old) { |
| 703 // we only care if there are registered side-effects |
| 704 var callbackName = this.observe[name]; |
| 705 if (callbackName) { |
| 706 // if we are observing the previous value, stop |
| 707 if (Array.isArray(old)) { |
| 708 log.observe && console.log('[%s] observeArrayValue: unregister observe
r [%s]', this.localName, name); |
| 709 this.unregisterObserver(name + '__array'); |
| 710 } |
| 711 // if the new value is an array, being observing it |
| 712 if (Array.isArray(value)) { |
| 713 log.observe && console.log('[%s] observeArrayValue: register observer
[%s]', this.localName, name, value); |
| 714 var observer = new ArrayObserver(value); |
| 715 observer.open(function(value, old) { |
| 716 this.invokeMethod(callbackName, [old]); |
| 717 }, this); |
| 718 this.registerObserver(name + '__array', observer); |
| 719 } |
| 720 } |
| 721 }, |
| 722 bindProperty: function(property, observable) { |
| 723 // apply Polymer two-way reference binding |
| 724 return bindProperties(this, property, observable); |
| 725 }, |
| 726 unbindAllProperties: function() { |
| 727 if (this._propertyObserver) { |
| 728 this._propertyObserver.close(); |
| 729 } |
| 730 this.unregisterObservers(); |
| 731 }, |
| 732 unbindProperty: function(name) { |
| 733 return this.unregisterObserver(name); |
| 734 }, |
| 735 invokeMethod: function(method, args) { |
| 736 var fn = this[method] || method; |
| 737 if (typeof fn === 'function') { |
| 738 fn.apply(this, args); |
| 739 } |
| 740 }, |
| 741 // bookkeeping observers for memory management |
| 742 registerObserver: function(name, observer) { |
| 743 var o$ = this._observers || (this._observers = {}); |
| 744 o$[name] = observer; |
| 745 }, |
| 746 unregisterObserver: function(name) { |
| 747 var o$ = this._observers; |
| 748 if (o$ && o$[name]) { |
| 749 o$[name].close(); |
| 750 o$[name] = null; |
| 751 return true; |
| 752 } |
| 753 }, |
| 754 unregisterObservers: function() { |
| 755 if (this._observers) { |
| 756 var keys=Object.keys(this._observers); |
| 757 for (var i=0, l=keys.length, k, o; (i < l) && (k=keys[i]); i++) { |
| 758 o = this._observers[k]; |
| 759 o.close(); |
| 760 } |
| 761 this._observers = {}; |
| 762 } |
| 763 } |
| 764 }; |
| 765 |
| 766 // property binding |
| 767 // bind a property in A to a path in B by converting A[property] to a |
| 768 // getter/setter pair that accesses B[...path...] |
| 769 function bindProperties(inA, inProperty, observable) { |
| 770 log.bind && console.log(LOG_BIND_PROPS, inB.localName || 'object', inPath, i
nA.localName, inProperty); |
| 771 // capture A's value if B's value is null or undefined, |
| 772 // otherwise use B's value |
| 773 // TODO(sorvell): need to review, can do with ObserverTransform |
| 774 var v = observable.discardChanges(); |
| 775 if (v === null || v === undefined) { |
| 776 observable.setValue(inA[inProperty]); |
| 777 } |
| 778 return Observer.defineComputedProperty(inA, inProperty, observable); |
| 779 } |
| 780 |
| 781 // logging |
| 782 var LOG_OBSERVE = '[%s] watching [%s]'; |
| 783 var LOG_OBSERVED = '[%s#%s] watch: [%s] now [%s] was [%s]'; |
| 784 var LOG_CHANGED = '[%s#%s] propertyChanged: [%s] now [%s] was [%s]'; |
| 785 var LOG_BIND_PROPS = "[%s]: bindProperties: [%s] to [%s].[%s]"; |
| 786 |
| 787 // exports |
| 788 |
| 789 scope.api.instance.properties = properties; |
| 790 |
| 791 })(Polymer); |
| 792 |
| 793 /* |
| 794 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 795 * Use of this source code is governed by a BSD-style |
| 796 * license that can be found in the LICENSE file. |
| 797 */ |
| 798 (function(scope) { |
| 799 |
| 800 // imports |
| 801 |
| 802 var log = window.logFlags || 0; |
| 803 var events = scope.api.instance.events; |
| 804 |
| 805 var syntax = new PolymerExpressions(); |
| 806 syntax.resolveEventHandler = function(model, path, node) { |
| 807 var ctlr = findEventController(node); |
| 808 if (ctlr) { |
| 809 var fn = path.getValueFrom(ctlr); |
| 810 if (fn) { |
| 811 return fn.bind(ctlr); |
| 812 } |
| 813 } |
| 814 } |
| 815 |
| 816 // An event controller is the host element for the shadowRoot in which |
| 817 // the node exists, or the first ancestor with a 'lightDomController' |
| 818 // property. |
| 819 function findEventController(node) { |
| 820 while (node.parentNode) { |
| 821 if (node.lightDomController) { |
| 822 return node; |
| 823 } |
| 824 node = node.parentNode; |
| 825 } |
| 826 return node.host; |
| 827 }; |
| 828 |
| 829 // element api supporting mdv |
| 830 |
| 831 var mdv = { |
| 832 syntax: syntax, |
| 833 instanceTemplate: function(template) { |
| 834 return template.createInstance(this, this.syntax); |
| 835 }, |
| 836 bind: function(name, observable, oneTime) { |
| 837 // note: binding is a prepare signal. This allows us to be sure that any |
| 838 // property changes that occur as a result of binding will be observed. |
| 839 if (!this._elementPrepared) { |
| 840 this.prepareElement(); |
| 841 } |
| 842 var property = this.propertyForAttribute(name); |
| 843 if (!property) { |
| 844 // TODO(sjmiles): this mixin method must use the special form |
| 845 // of `super` installed by `mixinMethod` in declaration/prototype.js |
| 846 return this.mixinSuper(arguments); |
| 847 } else { |
| 848 // clean out the closets |
| 849 this.unbind(name); |
| 850 // use n-way Polymer binding |
| 851 var observer = this.bindProperty(property, observable); |
| 852 // stick path on observer so it's available via this.bindings |
| 853 observer.path = observable.path_; |
| 854 // reflect bound property to attribute when binding |
| 855 // to ensure binding is not left on attribute if property |
| 856 // does not update due to not changing. |
| 857 this.reflectPropertyToAttribute(property); |
| 858 return this.bindings[name] = observer; |
| 859 } |
| 860 }, |
| 861 asyncUnbindAll: function() { |
| 862 if (!this._unbound) { |
| 863 log.unbind && console.log('[%s] asyncUnbindAll', this.localName); |
| 864 this._unbindAllJob = this.job(this._unbindAllJob, this.unbindAll, 0); |
| 865 } |
| 866 }, |
| 867 unbindAll: function() { |
| 868 if (!this._unbound) { |
| 869 this.unbindAllProperties(); |
| 870 this.super(); |
| 871 // unbind shadowRoot |
| 872 var root = this.shadowRoot; |
| 873 while (root) { |
| 874 unbindNodeTree(root); |
| 875 root = root.olderShadowRoot; |
| 876 } |
| 877 this._unbound = true; |
| 878 } |
| 879 }, |
| 880 cancelUnbindAll: function(preventCascade) { |
| 881 if (this._unbound) { |
| 882 log.unbind && console.warn('[%s] already unbound, cannot cancel unbindAl
l', this.localName); |
| 883 return; |
| 884 } |
| 885 log.unbind && console.log('[%s] cancelUnbindAll', this.localName); |
| 886 if (this._unbindAllJob) { |
| 887 this._unbindAllJob = this._unbindAllJob.stop(); |
| 888 } |
| 889 // cancel unbinding our shadow tree iff we're not in the process of |
| 890 // cascading our tree (as we do, for example, when the element is inserted
). |
| 891 if (!preventCascade) { |
| 892 forNodeTree(this.shadowRoot, function(n) { |
| 893 if (n.cancelUnbindAll) { |
| 894 n.cancelUnbindAll(); |
| 895 } |
| 896 }); |
| 897 } |
| 898 } |
| 899 }; |
| 900 |
| 901 function unbindNodeTree(node) { |
| 902 forNodeTree(node, _nodeUnbindAll); |
| 903 } |
| 904 |
| 905 function _nodeUnbindAll(node) { |
| 906 node.unbindAll(); |
| 907 } |
| 908 |
| 909 function forNodeTree(node, callback) { |
| 910 if (node) { |
| 911 callback(node); |
| 912 for (var child = node.firstChild; child; child = child.nextSibling) { |
| 913 forNodeTree(child, callback); |
| 914 } |
| 915 } |
| 916 } |
| 917 |
| 918 var mustachePattern = /\{\{([^{}]*)}}/; |
| 919 |
| 920 // exports |
| 921 |
| 922 scope.bindPattern = mustachePattern; |
| 923 scope.api.instance.mdv = mdv; |
| 924 |
| 925 })(Polymer); |
| 926 |
| 927 /* |
| 928 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 929 * Use of this source code is governed by a BSD-style |
| 930 * license that can be found in the LICENSE file. |
| 931 */ |
| 932 (function(scope) { |
| 933 var preparingElements = 0; |
| 934 |
| 935 var base = { |
| 936 PolymerBase: true, |
| 937 job: Polymer.job, |
| 938 super: Polymer.super, |
| 939 // user entry point for element has had its createdCallback called |
| 940 created: function() { |
| 941 }, |
| 942 // user entry point for element has shadowRoot and is ready for |
| 943 // api interaction |
| 944 ready: function() { |
| 945 }, |
| 946 createdCallback: function() { |
| 947 this.created(); |
| 948 if (this.ownerDocument.defaultView || this.alwaysPrepare || |
| 949 preparingElements > 0) { |
| 950 this.prepareElement(); |
| 951 } |
| 952 }, |
| 953 // system entry point, do not override |
| 954 prepareElement: function() { |
| 955 this._elementPrepared = true; |
| 956 // install shadowRoots storage |
| 957 this.shadowRoots = {}; |
| 958 // install property observers |
| 959 this.observeProperties(); |
| 960 // install boilerplate attributes |
| 961 this.copyInstanceAttributes(); |
| 962 // process input attributes |
| 963 this.takeAttributes(); |
| 964 // add event listeners |
| 965 this.addHostListeners(); |
| 966 // guarantees that while preparing, any |
| 967 // sub-elements are also prepared |
| 968 preparingElements++; |
| 969 // process declarative resources |
| 970 this.parseDeclarations(this.__proto__); |
| 971 // decrement semaphore |
| 972 preparingElements--; |
| 973 // TODO(sorvell): CE polyfill uses unresolved attribute to simulate |
| 974 // :unresolved; remove this attribute to be compatible with native |
| 975 // CE. |
| 976 this.removeAttribute('unresolved'); |
| 977 // user entry point |
| 978 this.ready(); |
| 979 }, |
| 980 attachedCallback: function() { |
| 981 if (!this._elementPrepared) { |
| 982 this.prepareElement(); |
| 983 } |
| 984 this.cancelUnbindAll(true); |
| 985 // invoke user action |
| 986 if (this.attached) { |
| 987 this.attached(); |
| 988 } |
| 989 // TODO(sorvell): bc |
| 990 if (this.enteredView) { |
| 991 this.enteredView(); |
| 992 } |
| 993 // NOTE: domReady can be used to access elements in dom (descendants, |
| 994 // ancestors, siblings) such that the developer is enured to upgrade |
| 995 // ordering. If the element definitions have loaded, domReady |
| 996 // can be used to access upgraded elements. |
| 997 if (!this.hasBeenAttached) { |
| 998 this.hasBeenAttached = true; |
| 999 if (this.domReady) { |
| 1000 this.async('domReady'); |
| 1001 } |
| 1002 } |
| 1003 }, |
| 1004 detachedCallback: function() { |
| 1005 if (!this.preventDispose) { |
| 1006 this.asyncUnbindAll(); |
| 1007 } |
| 1008 // invoke user action |
| 1009 if (this.detached) { |
| 1010 this.detached(); |
| 1011 } |
| 1012 // TODO(sorvell): bc |
| 1013 if (this.leftView) { |
| 1014 this.leftView(); |
| 1015 } |
| 1016 }, |
| 1017 // TODO(sorvell): bc |
| 1018 enteredViewCallback: function() { |
| 1019 this.attachedCallback(); |
| 1020 }, |
| 1021 // TODO(sorvell): bc |
| 1022 leftViewCallback: function() { |
| 1023 this.detachedCallback(); |
| 1024 }, |
| 1025 // TODO(sorvell): bc |
| 1026 enteredDocumentCallback: function() { |
| 1027 this.attachedCallback(); |
| 1028 }, |
| 1029 // TODO(sorvell): bc |
| 1030 leftDocumentCallback: function() { |
| 1031 this.detachedCallback(); |
| 1032 }, |
| 1033 // recursive ancestral <element> initialization, oldest first |
| 1034 parseDeclarations: function(p) { |
| 1035 if (p && p.element) { |
| 1036 this.parseDeclarations(p.__proto__); |
| 1037 p.parseDeclaration.call(this, p.element); |
| 1038 } |
| 1039 }, |
| 1040 // parse input <element> as needed, override for custom behavior |
| 1041 parseDeclaration: function(elementElement) { |
| 1042 var template = this.fetchTemplate(elementElement); |
| 1043 if (template) { |
| 1044 var root = this.shadowFromTemplate(template); |
| 1045 this.shadowRoots[elementElement.name] = root; |
| 1046 } |
| 1047 }, |
| 1048 // return a shadow-root template (if desired), override for custom behavior |
| 1049 fetchTemplate: function(elementElement) { |
| 1050 return elementElement.querySelector('template'); |
| 1051 }, |
| 1052 // utility function that creates a shadow root from a <template> |
| 1053 shadowFromTemplate: function(template) { |
| 1054 if (template) { |
| 1055 // make a shadow root |
| 1056 var root = this.createShadowRoot(); |
| 1057 // migrate flag(s) |
| 1058 root.resetStyleInheritance = this.resetStyleInheritance; |
| 1059 // stamp template |
| 1060 // which includes parsing and applying MDV bindings before being |
| 1061 // inserted (to avoid {{}} in attribute values) |
| 1062 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. |
| 1063 var dom = this.instanceTemplate(template); |
| 1064 // append to shadow dom |
| 1065 root.appendChild(dom); |
| 1066 // perform post-construction initialization tasks on shadow root |
| 1067 this.shadowRootReady(root, template); |
| 1068 // return the created shadow root |
| 1069 return root; |
| 1070 } |
| 1071 }, |
| 1072 // utility function that stamps a <template> into light-dom |
| 1073 lightFromTemplate: function(template) { |
| 1074 if (template) { |
| 1075 // TODO(sorvell): mark this element as a lightDOMController so that |
| 1076 // event listeners on bound nodes inside it will be called on it. |
| 1077 // Note, the expectation here is that events on all descendants |
| 1078 // should be handled by this element. |
| 1079 this.lightDomController = true; |
| 1080 // stamp template |
| 1081 // which includes parsing and applying MDV bindings before being |
| 1082 // inserted (to avoid {{}} in attribute values) |
| 1083 // e.g. to prevent <img src="images/{{icon}}"> from generating a 404. |
| 1084 var dom = this.instanceTemplate(template); |
| 1085 // append to shadow dom |
| 1086 this.appendChild(dom); |
| 1087 // perform post-construction initialization tasks on ahem, light root |
| 1088 this.shadowRootReady(this, template); |
| 1089 // return the created shadow root |
| 1090 return dom; |
| 1091 } |
| 1092 }, |
| 1093 shadowRootReady: function(root, template) { |
| 1094 // locate nodes with id and store references to them in this.$ hash |
| 1095 this.marshalNodeReferences(root); |
| 1096 // set up pointer gestures |
| 1097 PointerGestures.register(root); |
| 1098 }, |
| 1099 // locate nodes with id and store references to them in this.$ hash |
| 1100 marshalNodeReferences: function(root) { |
| 1101 // establish $ instance variable |
| 1102 var $ = this.$ = this.$ || {}; |
| 1103 // populate $ from nodes with ID from the LOCAL tree |
| 1104 if (root) { |
| 1105 var n$ = root.querySelectorAll("[id]"); |
| 1106 for (var i=0, l=n$.length, n; (i<l) && (n=n$[i]); i++) { |
| 1107 $[n.id] = n; |
| 1108 }; |
| 1109 } |
| 1110 }, |
| 1111 attributeChangedCallback: function(name, oldValue) { |
| 1112 // TODO(sjmiles): adhoc filter |
| 1113 if (name !== 'class' && name !== 'style') { |
| 1114 this.attributeToProperty(name, this.getAttribute(name)); |
| 1115 } |
| 1116 if (this.attributeChanged) { |
| 1117 this.attributeChanged.apply(this, arguments); |
| 1118 } |
| 1119 }, |
| 1120 onMutation: function(node, listener) { |
| 1121 var observer = new MutationObserver(function(mutations) { |
| 1122 listener.call(this, observer, mutations); |
| 1123 observer.disconnect(); |
| 1124 }.bind(this)); |
| 1125 observer.observe(node, {childList: true, subtree: true}); |
| 1126 } |
| 1127 }; |
| 1128 |
| 1129 // true if object has own PolymerBase api |
| 1130 function isBase(object) { |
| 1131 return object.hasOwnProperty('PolymerBase') |
| 1132 } |
| 1133 |
| 1134 // name a base constructor for dev tools |
| 1135 |
| 1136 function PolymerBase() {}; |
| 1137 PolymerBase.prototype = base; |
| 1138 base.constructor = PolymerBase; |
| 1139 |
| 1140 // exports |
| 1141 |
| 1142 scope.Base = PolymerBase; |
| 1143 scope.isBase = isBase; |
| 1144 scope.api.instance.base = base; |
| 1145 |
| 1146 })(Polymer); |
| 1147 |
| 1148 /* |
| 1149 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1150 * Use of this source code is governed by a BSD-style |
| 1151 * license that can be found in the LICENSE file. |
| 1152 */ |
| 1153 (function(scope) { |
| 1154 |
| 1155 // imports |
| 1156 |
| 1157 var log = window.logFlags || {}; |
| 1158 |
| 1159 // magic words |
| 1160 |
| 1161 var STYLE_SCOPE_ATTRIBUTE = 'element'; |
| 1162 var STYLE_CONTROLLER_SCOPE = 'controller'; |
| 1163 |
| 1164 var styles = { |
| 1165 STYLE_SCOPE_ATTRIBUTE: STYLE_SCOPE_ATTRIBUTE, |
| 1166 /** |
| 1167 * Installs external stylesheets and <style> elements with the attribute |
| 1168 * polymer-scope='controller' into the scope of element. This is intended |
| 1169 * to be a called during custom element construction. Note, this incurs a |
| 1170 * per instance cost and should be used sparingly. |
| 1171 * |
| 1172 * The need for this type of styling should go away when the shadowDOM spec |
| 1173 * addresses these issues: |
| 1174 * |
| 1175 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21391 |
| 1176 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21390 |
| 1177 * https://www.w3.org/Bugs/Public/show_bug.cgi?id=21389 |
| 1178 * |
| 1179 * @param element The custom element instance into whose controller (parent) |
| 1180 * scope styles will be installed. |
| 1181 * @param elementElement The <element> containing controller styles. |
| 1182 */ |
| 1183 // TODO(sorvell): remove when spec issues are addressed |
| 1184 installControllerStyles: function() { |
| 1185 // apply controller styles, but only if they are not yet applied |
| 1186 var scope = this.findStyleController(); |
| 1187 if (scope && !this.scopeHasElementStyle(scope, STYLE_CONTROLLER_SCOPE)) { |
| 1188 // allow inherited controller styles |
| 1189 var proto = getPrototypeOf(this), cssText = ''; |
| 1190 while (proto && proto.element) { |
| 1191 cssText += proto.element.cssTextForScope(STYLE_CONTROLLER_SCOPE); |
| 1192 proto = getPrototypeOf(proto); |
| 1193 } |
| 1194 if (cssText) { |
| 1195 var style = this.element.cssTextToScopeStyle(cssText, |
| 1196 STYLE_CONTROLLER_SCOPE); |
| 1197 // TODO(sorvell): for now these styles are not shimmed |
| 1198 // but we may need to shim them |
| 1199 Polymer.applyStyleToScope(style, scope); |
| 1200 } |
| 1201 } |
| 1202 }, |
| 1203 findStyleController: function() { |
| 1204 if (window.ShadowDOMPolyfill) { |
| 1205 return wrap(document.head); |
| 1206 } else { |
| 1207 // find the shadow root that contains this element |
| 1208 var n = this; |
| 1209 while (n.parentNode) { |
| 1210 n = n.parentNode; |
| 1211 } |
| 1212 return n === document ? document.head : n; |
| 1213 } |
| 1214 }, |
| 1215 scopeHasElementStyle: function(scope, descriptor) { |
| 1216 var rule = STYLE_SCOPE_ATTRIBUTE + '=' + this.localName + '-' + descriptor
; |
| 1217 return scope.querySelector('style[' + rule + ']'); |
| 1218 } |
| 1219 }; |
| 1220 |
| 1221 // NOTE: use raw prototype traversal so that we ensure correct traversal |
| 1222 // on platforms where the protoype chain is simulated via __proto__ (IE10) |
| 1223 function getPrototypeOf(prototype) { |
| 1224 return prototype.__proto__; |
| 1225 } |
| 1226 |
| 1227 // exports |
| 1228 |
| 1229 scope.api.instance.styles = styles; |
| 1230 |
| 1231 })(Polymer); |
| 1232 |
| 1233 /* |
| 1234 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1235 * Use of this source code is governed by a BSD-style |
| 1236 * license that can be found in the LICENSE file. |
| 1237 */ |
| 1238 (function(scope) { |
| 1239 |
| 1240 // imports |
| 1241 |
| 1242 var extend = scope.extend; |
| 1243 var api = scope.api; |
| 1244 |
| 1245 // imperative implementation: Polymer() |
| 1246 |
| 1247 // specify an 'own' prototype for tag `name` |
| 1248 function element(name, prototype) { |
| 1249 if (getRegisteredPrototype[name]) { |
| 1250 throw 'Already registered (Polymer) prototype for element ' + name; |
| 1251 } |
| 1252 // cache the prototype |
| 1253 registerPrototype(name, prototype); |
| 1254 // notify the registrar waiting for 'name', if any |
| 1255 notifyPrototype(name); |
| 1256 } |
| 1257 |
| 1258 // async prototype source |
| 1259 |
| 1260 function waitingForPrototype(name, client) { |
| 1261 waitPrototype[name] = client; |
| 1262 } |
| 1263 |
| 1264 var waitPrototype = {}; |
| 1265 |
| 1266 function notifyPrototype(name) { |
| 1267 if (waitPrototype[name]) { |
| 1268 waitPrototype[name].registerWhenReady(); |
| 1269 delete waitPrototype[name]; |
| 1270 } |
| 1271 } |
| 1272 |
| 1273 // utility and bookkeeping |
| 1274 |
| 1275 // maps tag names to prototypes, as registered with |
| 1276 // Polymer. Prototypes associated with a tag name |
| 1277 // using document.registerElement are available from |
| 1278 // HTMLElement.getPrototypeForTag(). |
| 1279 // If an element was fully registered by Polymer, then |
| 1280 // Polymer.getRegisteredPrototype(name) === |
| 1281 // HTMLElement.getPrototypeForTag(name) |
| 1282 |
| 1283 var prototypesByName = {}; |
| 1284 |
| 1285 function registerPrototype(name, prototype) { |
| 1286 return prototypesByName[name] = prototype || {}; |
| 1287 } |
| 1288 |
| 1289 function getRegisteredPrototype(name) { |
| 1290 return prototypesByName[name]; |
| 1291 } |
| 1292 |
| 1293 // exports |
| 1294 |
| 1295 scope.getRegisteredPrototype = getRegisteredPrototype; |
| 1296 scope.waitingForPrototype = waitingForPrototype; |
| 1297 |
| 1298 // namespace shenanigans so we can expose our scope on the registration |
| 1299 // function |
| 1300 |
| 1301 // make window.Polymer reference `element()` |
| 1302 |
| 1303 window.Polymer = element; |
| 1304 |
| 1305 // TODO(sjmiles): find a way to do this that is less terrible |
| 1306 // copy window.Polymer properties onto `element()` |
| 1307 |
| 1308 extend(Polymer, scope); |
| 1309 |
| 1310 // Under the HTMLImports polyfill, scripts in the main document |
| 1311 // do not block on imports; we want to allow calls to Polymer in the main |
| 1312 // document. Platform collects those calls until we can process them, which |
| 1313 // we do here. |
| 1314 |
| 1315 var declarations = Platform.deliverDeclarations(); |
| 1316 if (declarations) { |
| 1317 for (var i=0, l=declarations.length, d; (i<l) && (d=declarations[i]); i++) { |
| 1318 element.apply(null, d); |
| 1319 } |
| 1320 } |
| 1321 |
| 1322 })(Polymer); |
| 1323 |
| 1324 /* |
| 1325 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1326 * Use of this source code is governed by a BSD-style |
| 1327 * license that can be found in the LICENSE file. |
| 1328 */ |
| 1329 |
| 1330 (function(scope) { |
| 1331 |
| 1332 var path = { |
| 1333 resolveElementPaths: function(node) { |
| 1334 Platform.urlResolver.resolveDom(node); |
| 1335 }, |
| 1336 addResolvePathApi: function() { |
| 1337 // let assetpath attribute modify the resolve path |
| 1338 var assetPath = this.getAttribute('assetpath') || ''; |
| 1339 var root = new URL(assetPath, this.ownerDocument.baseURI); |
| 1340 this.prototype.resolvePath = function(urlPath, base) { |
| 1341 var u = new URL(urlPath, base || root); |
| 1342 return u.href; |
| 1343 }; |
| 1344 } |
| 1345 }; |
| 1346 |
| 1347 // exports |
| 1348 scope.api.declaration.path = path; |
| 1349 |
| 1350 })(Polymer); |
| 1351 |
| 1352 /* |
| 1353 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1354 * Use of this source code is governed by a BSD-style |
| 1355 * license that can be found in the LICENSE file. |
| 1356 */ |
| 1357 (function(scope) { |
| 1358 |
| 1359 // imports |
| 1360 |
| 1361 var log = window.logFlags || {}; |
| 1362 var api = scope.api.instance.styles; |
| 1363 var STYLE_SCOPE_ATTRIBUTE = api.STYLE_SCOPE_ATTRIBUTE; |
| 1364 |
| 1365 // magic words |
| 1366 |
| 1367 var STYLE_SELECTOR = 'style'; |
| 1368 var STYLE_LOADABLE_MATCH = '@import'; |
| 1369 var SHEET_SELECTOR = 'link[rel=stylesheet]'; |
| 1370 var STYLE_GLOBAL_SCOPE = 'global'; |
| 1371 var SCOPE_ATTR = 'polymer-scope'; |
| 1372 |
| 1373 var styles = { |
| 1374 // returns true if resources are loading |
| 1375 loadStyles: function(callback) { |
| 1376 var content = this.templateContent(); |
| 1377 if (content) { |
| 1378 this.convertSheetsToStyles(content); |
| 1379 } |
| 1380 var styles = this.findLoadableStyles(content); |
| 1381 if (styles.length) { |
| 1382 Platform.styleResolver.loadStyles(styles, callback); |
| 1383 } else if (callback) { |
| 1384 callback(); |
| 1385 } |
| 1386 }, |
| 1387 convertSheetsToStyles: function(root) { |
| 1388 var s$ = root.querySelectorAll(SHEET_SELECTOR); |
| 1389 for (var i=0, l=s$.length, s, c; (i<l) && (s=s$[i]); i++) { |
| 1390 c = createStyleElement(importRuleForSheet(s, this.ownerDocument.baseURI)
, |
| 1391 this.ownerDocument); |
| 1392 this.copySheetAttributes(c, s); |
| 1393 s.parentNode.replaceChild(c, s); |
| 1394 } |
| 1395 }, |
| 1396 copySheetAttributes: function(style, link) { |
| 1397 for (var i=0, a$=link.attributes, l=a$.length, a; (a=a$[i]) && i<l; i++) { |
| 1398 if (a.name !== 'rel' && a.name !== 'src') { |
| 1399 style.setAttribute(a.name, a.value); |
| 1400 } |
| 1401 } |
| 1402 }, |
| 1403 findLoadableStyles: function(root) { |
| 1404 var loadables = []; |
| 1405 if (root) { |
| 1406 var s$ = root.querySelectorAll(STYLE_SELECTOR); |
| 1407 for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) { |
| 1408 if (s.textContent.match(STYLE_LOADABLE_MATCH)) { |
| 1409 loadables.push(s); |
| 1410 } |
| 1411 } |
| 1412 } |
| 1413 return loadables; |
| 1414 }, |
| 1415 /** |
| 1416 * Install external stylesheets loaded in <polymer-element> elements into th
e |
| 1417 * element's template. |
| 1418 * @param elementElement The <element> element to style. |
| 1419 */ |
| 1420 // TODO(sorvell): wip... caching and styles handling can probably be removed |
| 1421 // We need a scheme to ensure stylesheets are eagerly loaded without |
| 1422 // the creation of an element instance. Here are 2 options for handling this
: |
| 1423 // 1. create a dummy element with ShadowDOM in dom that includes ALL styles |
| 1424 // processed here. |
| 1425 // 2. place stylesheets outside the element template. This will allow |
| 1426 // imports to naturally load the sheets. Then at load time, we can remove |
| 1427 // the stylesheet from dom. |
| 1428 installSheets: function() { |
| 1429 this.cacheSheets(); |
| 1430 this.cacheStyles(); |
| 1431 this.installLocalSheets(); |
| 1432 this.installGlobalStyles(); |
| 1433 }, |
| 1434 /** |
| 1435 * Remove all sheets from element and store for later use. |
| 1436 */ |
| 1437 cacheSheets: function() { |
| 1438 this.sheets = this.findNodes(SHEET_SELECTOR); |
| 1439 this.sheets.forEach(function(s) { |
| 1440 if (s.parentNode) { |
| 1441 s.parentNode.removeChild(s); |
| 1442 } |
| 1443 }); |
| 1444 }, |
| 1445 cacheStyles: function() { |
| 1446 this.styles = this.findNodes(STYLE_SELECTOR + '[' + SCOPE_ATTR + ']'); |
| 1447 this.styles.forEach(function(s) { |
| 1448 if (s.parentNode) { |
| 1449 s.parentNode.removeChild(s); |
| 1450 } |
| 1451 }); |
| 1452 }, |
| 1453 /** |
| 1454 * Takes external stylesheets loaded in an <element> element and moves |
| 1455 * their content into a <style> element inside the <element>'s template. |
| 1456 * The sheet is then removed from the <element>. This is done only so |
| 1457 * that if the element is loaded in the main document, the sheet does |
| 1458 * not become active. |
| 1459 * Note, ignores sheets with the attribute 'polymer-scope'. |
| 1460 * @param elementElement The <element> element to style. |
| 1461 */ |
| 1462 installLocalSheets: function () { |
| 1463 var sheets = this.sheets.filter(function(s) { |
| 1464 return !s.hasAttribute(SCOPE_ATTR); |
| 1465 }); |
| 1466 var content = this.templateContent(); |
| 1467 if (content) { |
| 1468 var cssText = ''; |
| 1469 sheets.forEach(function(sheet) { |
| 1470 cssText += cssTextFromSheet(sheet) + '\n'; |
| 1471 }); |
| 1472 if (cssText) { |
| 1473 var style = createStyleElement(cssText, this.ownerDocument); |
| 1474 content.insertBefore(style, content.firstChild); |
| 1475 } |
| 1476 } |
| 1477 }, |
| 1478 findNodes: function(selector, matcher) { |
| 1479 var nodes = this.querySelectorAll(selector).array(); |
| 1480 var content = this.templateContent(); |
| 1481 if (content) { |
| 1482 var templateNodes = content.querySelectorAll(selector).array(); |
| 1483 nodes = nodes.concat(templateNodes); |
| 1484 } |
| 1485 return matcher ? nodes.filter(matcher) : nodes; |
| 1486 }, |
| 1487 templateContent: function() { |
| 1488 var template = this.querySelector('template'); |
| 1489 return template && templateContent(template); |
| 1490 }, |
| 1491 /** |
| 1492 * Promotes external stylesheets and <style> elements with the attribute |
| 1493 * polymer-scope='global' into global scope. |
| 1494 * This is particularly useful for defining @keyframe rules which |
| 1495 * currently do not function in scoped or shadow style elements. |
| 1496 * (See wkb.ug/72462) |
| 1497 * @param elementElement The <element> element to style. |
| 1498 */ |
| 1499 // TODO(sorvell): remove when wkb.ug/72462 is addressed. |
| 1500 installGlobalStyles: function() { |
| 1501 var style = this.styleForScope(STYLE_GLOBAL_SCOPE); |
| 1502 applyStyleToScope(style, document.head); |
| 1503 }, |
| 1504 cssTextForScope: function(scopeDescriptor) { |
| 1505 var cssText = ''; |
| 1506 // handle stylesheets |
| 1507 var selector = '[' + SCOPE_ATTR + '=' + scopeDescriptor + ']'; |
| 1508 var matcher = function(s) { |
| 1509 return matchesSelector(s, selector); |
| 1510 }; |
| 1511 var sheets = this.sheets.filter(matcher); |
| 1512 sheets.forEach(function(sheet) { |
| 1513 cssText += cssTextFromSheet(sheet) + '\n\n'; |
| 1514 }); |
| 1515 // handle cached style elements |
| 1516 var styles = this.styles.filter(matcher); |
| 1517 styles.forEach(function(style) { |
| 1518 cssText += style.textContent + '\n\n'; |
| 1519 }); |
| 1520 return cssText; |
| 1521 }, |
| 1522 styleForScope: function(scopeDescriptor) { |
| 1523 var cssText = this.cssTextForScope(scopeDescriptor); |
| 1524 return this.cssTextToScopeStyle(cssText, scopeDescriptor); |
| 1525 }, |
| 1526 cssTextToScopeStyle: function(cssText, scopeDescriptor) { |
| 1527 if (cssText) { |
| 1528 var style = createStyleElement(cssText); |
| 1529 style.setAttribute(STYLE_SCOPE_ATTRIBUTE, this.getAttribute('name') + |
| 1530 '-' + scopeDescriptor); |
| 1531 return style; |
| 1532 } |
| 1533 } |
| 1534 }; |
| 1535 |
| 1536 function importRuleForSheet(sheet, baseUrl) { |
| 1537 var href = new URL(sheet.getAttribute('href'), baseUrl).href; |
| 1538 return '@import \'' + href + '\';' |
| 1539 } |
| 1540 |
| 1541 function applyStyleToScope(style, scope) { |
| 1542 if (style) { |
| 1543 // TODO(sorvell): necessary for IE |
| 1544 // see https://connect.microsoft.com/IE/feedback/details/790212/ |
| 1545 // cloning-a-style-element-and-adding-to-document-produces |
| 1546 // -unexpected-result#details |
| 1547 // var clone = style.cloneNode(true); |
| 1548 var clone = createStyleElement(style.textContent); |
| 1549 var attr = style.getAttribute(STYLE_SCOPE_ATTRIBUTE); |
| 1550 if (attr) { |
| 1551 clone.setAttribute(STYLE_SCOPE_ATTRIBUTE, attr); |
| 1552 } |
| 1553 scope.appendChild(clone); |
| 1554 } |
| 1555 } |
| 1556 |
| 1557 function createStyleElement(cssText, scope) { |
| 1558 scope = scope || document; |
| 1559 scope = scope.createElement ? scope : scope.ownerDocument; |
| 1560 var style = scope.createElement('style'); |
| 1561 style.textContent = cssText; |
| 1562 return style; |
| 1563 } |
| 1564 |
| 1565 function cssTextFromSheet(sheet) { |
| 1566 return (sheet && sheet.__resource) || ''; |
| 1567 } |
| 1568 |
| 1569 function matchesSelector(node, inSelector) { |
| 1570 if (matches) { |
| 1571 return matches.call(node, inSelector); |
| 1572 } |
| 1573 } |
| 1574 var p = HTMLElement.prototype; |
| 1575 var matches = p.matches || p.matchesSelector || p.webkitMatchesSelector |
| 1576 || p.mozMatchesSelector; |
| 1577 |
| 1578 // exports |
| 1579 |
| 1580 scope.api.declaration.styles = styles; |
| 1581 scope.applyStyleToScope = applyStyleToScope; |
| 1582 |
| 1583 })(Polymer); |
| 1584 |
| 1585 /* |
| 1586 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1587 * Use of this source code is governed by a BSD-style |
| 1588 * license that can be found in the LICENSE file. |
| 1589 */ |
| 1590 |
| 1591 (function(scope) { |
| 1592 |
| 1593 // imports |
| 1594 |
| 1595 var log = window.logFlags || {}; |
| 1596 var api = scope.api.instance.events; |
| 1597 var EVENT_PREFIX = api.EVENT_PREFIX; |
| 1598 // polymer-element declarative api: events feature |
| 1599 |
| 1600 var events = { |
| 1601 parseHostEvents: function() { |
| 1602 // our delegates map |
| 1603 var delegates = this.prototype.eventDelegates; |
| 1604 // extract data from attributes into delegates |
| 1605 this.addAttributeDelegates(delegates); |
| 1606 }, |
| 1607 addAttributeDelegates: function(delegates) { |
| 1608 // for each attribute |
| 1609 for (var i=0, a; a=this.attributes[i]; i++) { |
| 1610 // does it have magic marker identifying it as an event delegate? |
| 1611 if (this.hasEventPrefix(a.name)) { |
| 1612 // if so, add the info to delegates |
| 1613 delegates[this.removeEventPrefix(a.name)] = a.value.replace('{{', '') |
| 1614 .replace('}}', '').trim(); |
| 1615 } |
| 1616 } |
| 1617 }, |
| 1618 // starts with 'on-' |
| 1619 hasEventPrefix: function (n) { |
| 1620 return n && (n[0] === 'o') && (n[1] === 'n') && (n[2] === '-'); |
| 1621 }, |
| 1622 removeEventPrefix: function(n) { |
| 1623 return n.slice(prefixLength); |
| 1624 } |
| 1625 }; |
| 1626 |
| 1627 var prefixLength = EVENT_PREFIX.length; |
| 1628 |
| 1629 // exports |
| 1630 scope.api.declaration.events = events; |
| 1631 |
| 1632 })(Polymer); |
| 1633 /* |
| 1634 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1635 * Use of this source code is governed by a BSD-style |
| 1636 * license that can be found in the LICENSE file. |
| 1637 */ |
| 1638 (function(scope) { |
| 1639 |
| 1640 // element api |
| 1641 |
| 1642 var properties = { |
| 1643 inferObservers: function(prototype) { |
| 1644 // called before prototype.observe is chained to inherited object |
| 1645 var observe = prototype.observe, property; |
| 1646 for (var n in prototype) { |
| 1647 if (n.slice(-7) === 'Changed') { |
| 1648 if (!observe) { |
| 1649 observe = (prototype.observe = {}); |
| 1650 } |
| 1651 property = n.slice(0, -7) |
| 1652 observe[property] = observe[property] || n; |
| 1653 } |
| 1654 } |
| 1655 }, |
| 1656 explodeObservers: function(prototype) { |
| 1657 // called before prototype.observe is chained to inherited object |
| 1658 var o = prototype.observe; |
| 1659 if (o) { |
| 1660 var exploded = {}; |
| 1661 for (var n in o) { |
| 1662 var names = n.split(' '); |
| 1663 for (var i=0, ni; ni=names[i]; i++) { |
| 1664 exploded[ni] = o[n]; |
| 1665 } |
| 1666 } |
| 1667 prototype.observe = exploded; |
| 1668 } |
| 1669 }, |
| 1670 optimizePropertyMaps: function(prototype) { |
| 1671 if (prototype.observe) { |
| 1672 // construct name list |
| 1673 var a = prototype._observeNames = []; |
| 1674 for (var n in prototype.observe) { |
| 1675 var names = n.split(' '); |
| 1676 for (var i=0, ni; ni=names[i]; i++) { |
| 1677 a.push(ni); |
| 1678 } |
| 1679 //a.push(n); |
| 1680 } |
| 1681 } |
| 1682 if (prototype.publish) { |
| 1683 // construct name list |
| 1684 var a = prototype._publishNames = []; |
| 1685 for (var n in prototype.publish) { |
| 1686 a.push(n); |
| 1687 } |
| 1688 } |
| 1689 }, |
| 1690 publishProperties: function(prototype, base) { |
| 1691 // if we have any properties to publish |
| 1692 var publish = prototype.publish; |
| 1693 if (publish) { |
| 1694 // transcribe `publish` entries onto own prototype |
| 1695 this.requireProperties(publish, prototype, base); |
| 1696 // construct map of lower-cased property names |
| 1697 prototype._publishLC = this.lowerCaseMap(publish); |
| 1698 } |
| 1699 }, |
| 1700 requireProperties: function(properties, prototype, base) { |
| 1701 // ensure a prototype value for each property |
| 1702 for (var n in properties) { |
| 1703 if (prototype[n] === undefined && base[n] === undefined) { |
| 1704 prototype[n] = properties[n]; |
| 1705 } |
| 1706 } |
| 1707 }, |
| 1708 lowerCaseMap: function(properties) { |
| 1709 var map = {}; |
| 1710 for (var n in properties) { |
| 1711 map[n.toLowerCase()] = n; |
| 1712 } |
| 1713 return map; |
| 1714 } |
| 1715 }; |
| 1716 |
| 1717 // exports |
| 1718 |
| 1719 scope.api.declaration.properties = properties; |
| 1720 |
| 1721 })(Polymer); |
| 1722 |
| 1723 /* |
| 1724 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1725 * Use of this source code is governed by a BSD-style |
| 1726 * license that can be found in the LICENSE file. |
| 1727 */ |
| 1728 (function(scope) { |
| 1729 |
| 1730 // magic words |
| 1731 |
| 1732 var ATTRIBUTES_ATTRIBUTE = 'attributes'; |
| 1733 var ATTRIBUTES_REGEX = /\s|,/; |
| 1734 |
| 1735 // attributes api |
| 1736 |
| 1737 var attributes = { |
| 1738 inheritAttributesObjects: function(prototype) { |
| 1739 // chain our lower-cased publish map to the inherited version |
| 1740 this.inheritObject(prototype, 'publishLC'); |
| 1741 // chain our instance attributes map to the inherited version |
| 1742 this.inheritObject(prototype, '_instanceAttributes'); |
| 1743 }, |
| 1744 publishAttributes: function(prototype, base) { |
| 1745 // merge names from 'attributes' attribute |
| 1746 var attributes = this.getAttribute(ATTRIBUTES_ATTRIBUTE); |
| 1747 if (attributes) { |
| 1748 // get properties to publish |
| 1749 var publish = prototype.publish || (prototype.publish = {}); |
| 1750 // names='a b c' or names='a,b,c' |
| 1751 var names = attributes.split(ATTRIBUTES_REGEX); |
| 1752 // record each name for publishing |
| 1753 for (var i=0, l=names.length, n; i<l; i++) { |
| 1754 // remove excess ws |
| 1755 n = names[i].trim(); |
| 1756 // do not override explicit entries |
| 1757 if (n && publish[n] === undefined && base[n] === undefined) { |
| 1758 publish[n] = null; |
| 1759 } |
| 1760 } |
| 1761 } |
| 1762 }, |
| 1763 // record clonable attributes from <element> |
| 1764 accumulateInstanceAttributes: function() { |
| 1765 // inherit instance attributes |
| 1766 var clonable = this.prototype._instanceAttributes; |
| 1767 // merge attributes from element |
| 1768 var a$ = this.attributes; |
| 1769 for (var i=0, l=a$.length, a; (i<l) && (a=a$[i]); i++) { |
| 1770 if (this.isInstanceAttribute(a.name)) { |
| 1771 clonable[a.name] = a.value; |
| 1772 } |
| 1773 } |
| 1774 }, |
| 1775 isInstanceAttribute: function(name) { |
| 1776 return !this.blackList[name] && name.slice(0,3) !== 'on-'; |
| 1777 }, |
| 1778 // do not clone these attributes onto instances |
| 1779 blackList: { |
| 1780 name: 1, |
| 1781 'extends': 1, |
| 1782 constructor: 1, |
| 1783 noscript: 1, |
| 1784 assetpath: 1, |
| 1785 'cache-csstext': 1 |
| 1786 } |
| 1787 }; |
| 1788 |
| 1789 // add ATTRIBUTES_ATTRIBUTE to the blacklist |
| 1790 attributes.blackList[ATTRIBUTES_ATTRIBUTE] = 1; |
| 1791 |
| 1792 // exports |
| 1793 |
| 1794 scope.api.declaration.attributes = attributes; |
| 1795 |
| 1796 })(Polymer); |
| 1797 |
| 1798 /* |
| 1799 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 1800 * Use of this source code is governed by a BSD-style |
| 1801 * license that can be found in the LICENSE file. |
| 1802 */ |
| 1803 (function(scope) { |
| 1804 |
| 1805 // imports |
| 1806 |
| 1807 var api = scope.api; |
| 1808 var isBase = scope.isBase; |
| 1809 var extend = scope.extend; |
| 1810 |
| 1811 // prototype api |
| 1812 |
| 1813 var prototype = { |
| 1814 |
| 1815 register: function(name, extendeeName) { |
| 1816 // build prototype combining extendee, Polymer base, and named api |
| 1817 this.buildPrototype(name, extendeeName); |
| 1818 // register our custom element with the platform |
| 1819 this.registerPrototype(name, extendeeName); |
| 1820 // reference constructor in a global named by 'constructor' attribute |
| 1821 this.publishConstructor(); |
| 1822 }, |
| 1823 |
| 1824 buildPrototype: function(name, extendeeName) { |
| 1825 // get our custom prototype (before chaining) |
| 1826 var extension = scope.getRegisteredPrototype(name); |
| 1827 // get basal prototype |
| 1828 var base = this.generateBasePrototype(extendeeName); |
| 1829 // implement declarative features |
| 1830 this.desugarBeforeChaining(extension, base); |
| 1831 // join prototypes |
| 1832 this.prototype = this.chainPrototypes(extension, base); |
| 1833 // more declarative features |
| 1834 this.desugarAfterChaining(name, extendeeName); |
| 1835 }, |
| 1836 |
| 1837 desugarBeforeChaining: function(prototype, base) { |
| 1838 // back reference declaration element |
| 1839 // TODO(sjmiles): replace `element` with `elementElement` or `declaration` |
| 1840 prototype.element = this; |
| 1841 // transcribe `attributes` declarations onto own prototype's `publish` |
| 1842 this.publishAttributes(prototype, base); |
| 1843 // `publish` properties to the prototype and to attribute watch |
| 1844 this.publishProperties(prototype, base); |
| 1845 // infer observers for `observe` list based on method names |
| 1846 this.inferObservers(prototype); |
| 1847 // desugar compound observer syntax, e.g. 'a b c' |
| 1848 this.explodeObservers(prototype); |
| 1849 }, |
| 1850 |
| 1851 chainPrototypes: function(prototype, base) { |
| 1852 // chain various meta-data objects to inherited versions |
| 1853 this.inheritMetaData(prototype, base); |
| 1854 // chain custom api to inherited |
| 1855 var chained = this.chainObject(prototype, base); |
| 1856 // x-platform fixup |
| 1857 ensurePrototypeTraversal(chained); |
| 1858 return chained; |
| 1859 }, |
| 1860 |
| 1861 inheritMetaData: function(prototype, base) { |
| 1862 // chain observe object to inherited |
| 1863 this.inheritObject('observe', prototype, base); |
| 1864 // chain publish object to inherited |
| 1865 this.inheritObject('publish', prototype, base); |
| 1866 // chain our lower-cased publish map to the inherited version |
| 1867 this.inheritObject('_publishLC', prototype, base); |
| 1868 // chain our instance attributes map to the inherited version |
| 1869 this.inheritObject('_instanceAttributes', prototype, base); |
| 1870 // chain our event delegates map to the inherited version |
| 1871 this.inheritObject('eventDelegates', prototype, base); |
| 1872 }, |
| 1873 |
| 1874 // implement various declarative features |
| 1875 desugarAfterChaining: function(name, extendee) { |
| 1876 // build side-chained lists to optimize iterations |
| 1877 this.optimizePropertyMaps(this.prototype); |
| 1878 // install external stylesheets as if they are inline |
| 1879 this.installSheets(); |
| 1880 // adjust any paths in dom from imports |
| 1881 this.resolveElementPaths(this); |
| 1882 // compile list of attributes to copy to instances |
| 1883 this.accumulateInstanceAttributes(); |
| 1884 // parse on-* delegates declared on `this` element |
| 1885 this.parseHostEvents(); |
| 1886 // |
| 1887 // install a helper method this.resolvePath to aid in |
| 1888 // setting resource urls. e.g. |
| 1889 // this.$.image.src = this.resolvePath('images/foo.png') |
| 1890 this.addResolvePathApi(); |
| 1891 // under ShadowDOMPolyfill, transforms to approximate missing CSS features |
| 1892 if (window.ShadowDOMPolyfill) { |
| 1893 Platform.ShadowCSS.shimStyling(this.templateContent(), name, extendee); |
| 1894 } |
| 1895 // allow custom element access to the declarative context |
| 1896 if (this.prototype.registerCallback) { |
| 1897 this.prototype.registerCallback(this); |
| 1898 } |
| 1899 }, |
| 1900 |
| 1901 // if a named constructor is requested in element, map a reference |
| 1902 // to the constructor to the given symbol |
| 1903 publishConstructor: function() { |
| 1904 var symbol = this.getAttribute('constructor'); |
| 1905 if (symbol) { |
| 1906 window[symbol] = this.ctor; |
| 1907 } |
| 1908 }, |
| 1909 |
| 1910 // build prototype combining extendee, Polymer base, and named api |
| 1911 generateBasePrototype: function(extnds) { |
| 1912 var prototype = this.findBasePrototype(extnds); |
| 1913 if (!prototype) { |
| 1914 // create a prototype based on tag-name extension |
| 1915 var prototype = HTMLElement.getPrototypeForTag(extnds); |
| 1916 // insert base api in inheritance chain (if needed) |
| 1917 prototype = this.ensureBaseApi(prototype); |
| 1918 // memoize this base |
| 1919 memoizedBases[extnds] = prototype; |
| 1920 } |
| 1921 return prototype; |
| 1922 }, |
| 1923 |
| 1924 findBasePrototype: function(name) { |
| 1925 return memoizedBases[name]; |
| 1926 }, |
| 1927 |
| 1928 // install Polymer instance api into prototype chain, as needed |
| 1929 ensureBaseApi: function(prototype) { |
| 1930 if (prototype.PolymerBase) { |
| 1931 return prototype; |
| 1932 } |
| 1933 var extended = Object.create(prototype); |
| 1934 // we need a unique copy of base api for each base prototype |
| 1935 // therefore we 'extend' here instead of simply chaining |
| 1936 api.publish(api.instance, extended); |
| 1937 // TODO(sjmiles): sharing methods across prototype chains is |
| 1938 // not supported by 'super' implementation which optimizes |
| 1939 // by memoizing prototype relationships. |
| 1940 // Probably we should have a version of 'extend' that is |
| 1941 // share-aware: it could study the text of each function, |
| 1942 // look for usage of 'super', and wrap those functions in |
| 1943 // closures. |
| 1944 // As of now, there is only one problematic method, so |
| 1945 // we just patch it manually. |
| 1946 // To avoid re-entrancy problems, the special super method |
| 1947 // installed is called `mixinSuper` and the mixin method |
| 1948 // must use this method instead of the default `super`. |
| 1949 this.mixinMethod(extended, prototype, api.instance.mdv, 'bind'); |
| 1950 // return buffed-up prototype |
| 1951 return extended; |
| 1952 }, |
| 1953 |
| 1954 mixinMethod: function(extended, prototype, api, name) { |
| 1955 var $super = function(args) { |
| 1956 return prototype[name].apply(this, args); |
| 1957 }; |
| 1958 extended[name] = function() { |
| 1959 this.mixinSuper = $super; |
| 1960 return api[name].apply(this, arguments); |
| 1961 } |
| 1962 }, |
| 1963 |
| 1964 // ensure prototype[name] inherits from a prototype.prototype[name] |
| 1965 inheritObject: function(name, prototype, base) { |
| 1966 // require an object |
| 1967 var source = prototype[name] || {}; |
| 1968 // chain inherited properties onto a new object |
| 1969 prototype[name] = this.chainObject(source, base[name]); |
| 1970 }, |
| 1971 |
| 1972 // register 'prototype' to custom element 'name', store constructor |
| 1973 registerPrototype: function(name, extendee) { |
| 1974 var info = { |
| 1975 prototype: this.prototype |
| 1976 } |
| 1977 // native element must be specified in extends |
| 1978 var typeExtension = this.findTypeExtension(extendee); |
| 1979 if (typeExtension) { |
| 1980 info.extends = typeExtension; |
| 1981 } |
| 1982 // register the prototype with HTMLElement for name lookup |
| 1983 HTMLElement.register(name, this.prototype); |
| 1984 // register the custom type |
| 1985 this.ctor = document.registerElement(name, info); |
| 1986 }, |
| 1987 |
| 1988 findTypeExtension: function(name) { |
| 1989 if (name && name.indexOf('-') < 0) { |
| 1990 return name; |
| 1991 } else { |
| 1992 var p = this.findBasePrototype(name); |
| 1993 if (p.element) { |
| 1994 return this.findTypeExtension(p.element.extends); |
| 1995 } |
| 1996 } |
| 1997 } |
| 1998 |
| 1999 }; |
| 2000 |
| 2001 // memoize base prototypes |
| 2002 var memoizedBases = {}; |
| 2003 |
| 2004 // implementation of 'chainObject' depends on support for __proto__ |
| 2005 if (Object.__proto__) { |
| 2006 prototype.chainObject = function(object, inherited) { |
| 2007 if (object && inherited && object !== inherited) { |
| 2008 object.__proto__ = inherited; |
| 2009 } |
| 2010 return object; |
| 2011 } |
| 2012 } else { |
| 2013 prototype.chainObject = function(object, inherited) { |
| 2014 if (object && inherited && object !== inherited) { |
| 2015 var chained = Object.create(inherited); |
| 2016 object = extend(chained, object); |
| 2017 } |
| 2018 return object; |
| 2019 } |
| 2020 } |
| 2021 |
| 2022 // On platforms that do not support __proto__ (versions of IE), the prototype |
| 2023 // chain of a custom element is simulated via installation of __proto__. |
| 2024 // Although custom elements manages this, we install it here so it's |
| 2025 // available during desugaring. |
| 2026 function ensurePrototypeTraversal(prototype) { |
| 2027 if (!Object.__proto__) { |
| 2028 var ancestor = Object.getPrototypeOf(prototype); |
| 2029 prototype.__proto__ = ancestor; |
| 2030 if (isBase(ancestor)) { |
| 2031 ancestor.__proto__ = Object.getPrototypeOf(ancestor); |
| 2032 } |
| 2033 } |
| 2034 } |
| 2035 |
| 2036 // exports |
| 2037 |
| 2038 api.declaration.prototype = prototype; |
| 2039 |
| 2040 })(Polymer); |
| 2041 |
| 2042 /* |
| 2043 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 2044 * Use of this source code is governed by a BSD-style |
| 2045 * license that can be found in the LICENSE file. |
| 2046 */ |
| 2047 (function(scope) { |
| 2048 |
| 2049 var queue = { |
| 2050 // tell the queue to wait for an element to be ready |
| 2051 wait: function(element, check, go) { |
| 2052 if (this.indexOf(element) === -1) { |
| 2053 this.add(element); |
| 2054 element.__check = check; |
| 2055 element.__go = go; |
| 2056 } |
| 2057 return (this.indexOf(element) !== 0); |
| 2058 }, |
| 2059 add: function(element) { |
| 2060 //console.log('queueing', element.name); |
| 2061 queueForElement(element).push(element); |
| 2062 }, |
| 2063 indexOf: function(element) { |
| 2064 var i = queueForElement(element).indexOf(element); |
| 2065 if (i >= 0 && document.contains(element)) { |
| 2066 i += (HTMLImports.useNative || HTMLImports.ready) ? importQueue.length : |
| 2067 1e9; |
| 2068 } |
| 2069 return i; |
| 2070 }, |
| 2071 // tell the queue an element is ready to be registered |
| 2072 go: function(element) { |
| 2073 var readied = this.remove(element); |
| 2074 if (readied) { |
| 2075 readied.__go.call(readied); |
| 2076 readied.__check = readied.__go = null; |
| 2077 this.check(); |
| 2078 } |
| 2079 }, |
| 2080 remove: function(element) { |
| 2081 var i = this.indexOf(element); |
| 2082 if (i !== 0) { |
| 2083 //console.warn('queue order wrong', i); |
| 2084 return; |
| 2085 } |
| 2086 return queueForElement(element).shift(); |
| 2087 }, |
| 2088 check: function() { |
| 2089 // next |
| 2090 var element = this.nextElement(); |
| 2091 if (element) { |
| 2092 element.__check.call(element); |
| 2093 } |
| 2094 if (this.canReady()) { |
| 2095 this.ready(); |
| 2096 return true; |
| 2097 } |
| 2098 }, |
| 2099 nextElement: function() { |
| 2100 return nextQueued(); |
| 2101 }, |
| 2102 canReady: function() { |
| 2103 return !this.waitToReady && this.isEmpty(); |
| 2104 }, |
| 2105 isEmpty: function() { |
| 2106 return !importQueue.length && !mainQueue.length; |
| 2107 }, |
| 2108 ready: function() { |
| 2109 // TODO(sorvell): As an optimization, turn off CE polyfill upgrading |
| 2110 // while registering. This way we avoid having to upgrade each document |
| 2111 // piecemeal per registration and can instead register all elements |
| 2112 // and upgrade once in a batch. Without this optimization, upgrade time |
| 2113 // degrades significantly when SD polyfill is used. This is mainly because |
| 2114 // querying the document tree for elements is slow under the SD polyfill. |
| 2115 if (CustomElements.ready === false) { |
| 2116 CustomElements.upgradeDocumentTree(document); |
| 2117 CustomElements.ready = true; |
| 2118 } |
| 2119 if (readyCallbacks) { |
| 2120 var fn; |
| 2121 while (readyCallbacks.length) { |
| 2122 fn = readyCallbacks.shift(); |
| 2123 fn(); |
| 2124 } |
| 2125 } |
| 2126 }, |
| 2127 addReadyCallback: function(callback) { |
| 2128 if (callback) { |
| 2129 readyCallbacks.push(callback); |
| 2130 } |
| 2131 }, |
| 2132 waitToReady: true |
| 2133 }; |
| 2134 |
| 2135 var importQueue = []; |
| 2136 var mainQueue = []; |
| 2137 var readyCallbacks = []; |
| 2138 |
| 2139 function queueForElement(element) { |
| 2140 return document.contains(element) ? mainQueue : importQueue; |
| 2141 } |
| 2142 |
| 2143 function nextQueued() { |
| 2144 return importQueue.length ? importQueue[0] : mainQueue[0]; |
| 2145 } |
| 2146 |
| 2147 var polymerReadied = false; |
| 2148 |
| 2149 document.addEventListener('WebComponentsReady', function() { |
| 2150 CustomElements.ready = false; |
| 2151 }); |
| 2152 |
| 2153 function whenPolymerReady(callback) { |
| 2154 queue.waitToReady = true; |
| 2155 CustomElements.ready = false; |
| 2156 HTMLImports.whenImportsReady(function() { |
| 2157 queue.addReadyCallback(callback); |
| 2158 queue.waitToReady = false; |
| 2159 queue.check(); |
| 2160 }); |
| 2161 } |
| 2162 |
| 2163 // exports |
| 2164 scope.queue = queue; |
| 2165 scope.whenPolymerReady = whenPolymerReady; |
| 2166 })(Polymer); |
| 2167 |
| 2168 /* |
| 2169 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 2170 * Use of this source code is governed by a BSD-style |
| 2171 * license that can be found in the LICENSE file. |
| 2172 */ |
| 2173 (function(scope) { |
| 2174 |
| 2175 var whenPolymerReady = scope.whenPolymerReady; |
| 2176 |
| 2177 function importElements(elementOrFragment, callback) { |
| 2178 if (elementOrFragment) { |
| 2179 document.head.appendChild(elementOrFragment); |
| 2180 whenPolymerReady(callback); |
| 2181 } else if (callback) { |
| 2182 callback(); |
| 2183 } |
| 2184 } |
| 2185 |
| 2186 function importUrls(urls, callback) { |
| 2187 if (urls && urls.length) { |
| 2188 var frag = document.createDocumentFragment(); |
| 2189 for (var i=0, l=urls.length, url, link; (i<l) && (url=urls[i]); i++) { |
| 2190 link = document.createElement('link'); |
| 2191 link.rel = 'import'; |
| 2192 link.href = url; |
| 2193 frag.appendChild(link); |
| 2194 } |
| 2195 importElements(frag, callback); |
| 2196 } else if (callback) { |
| 2197 callback(); |
| 2198 } |
| 2199 } |
| 2200 |
| 2201 // exports |
| 2202 scope.import = importUrls; |
| 2203 scope.importElements = importElements; |
| 2204 |
| 2205 })(Polymer); |
| 2206 |
| 2207 /* |
| 2208 * Copyright 2013 The Polymer Authors. All rights reserved. |
| 2209 * Use of this source code is governed by a BSD-style |
| 2210 * license that can be found in the LICENSE file. |
| 2211 */ |
| 2212 (function(scope) { |
| 2213 |
| 2214 // imports |
| 2215 |
| 2216 var extend = scope.extend; |
| 2217 var api = scope.api; |
| 2218 var queue = scope.queue; |
| 2219 var whenPolymerReady = scope.whenPolymerReady; |
| 2220 var getRegisteredPrototype = scope.getRegisteredPrototype; |
| 2221 var waitingForPrototype = scope.waitingForPrototype; |
| 2222 |
| 2223 // declarative implementation: <polymer-element> |
| 2224 |
| 2225 var prototype = extend(Object.create(HTMLElement.prototype), { |
| 2226 |
| 2227 createdCallback: function() { |
| 2228 if (this.getAttribute('name')) { |
| 2229 this.init(); |
| 2230 } |
| 2231 }, |
| 2232 |
| 2233 init: function() { |
| 2234 // fetch declared values |
| 2235 this.name = this.getAttribute('name'); |
| 2236 this.extends = this.getAttribute('extends'); |
| 2237 // initiate any async resource fetches |
| 2238 this.loadResources(); |
| 2239 // register when all constraints are met |
| 2240 this.registerWhenReady(); |
| 2241 }, |
| 2242 |
| 2243 registerWhenReady: function() { |
| 2244 if (this.registered |
| 2245 || this.waitingForPrototype(this.name) |
| 2246 || this.waitingForQueue() |
| 2247 || this.waitingForResources()) { |
| 2248 return; |
| 2249 } |
| 2250 queue.go(this); |
| 2251 }, |
| 2252 |
| 2253 |
| 2254 // TODO(sorvell): refactor, this method is private-ish, but it's being |
| 2255 // called by the queue object. |
| 2256 _register: function() { |
| 2257 //console.log('registering', this.name); |
| 2258 //console.group('registering', this.name); |
| 2259 // warn if extending from a custom element not registered via Polymer |
| 2260 if (isCustomTag(this.extends) && !isRegistered(this.extends)) { |
| 2261 console.warn('%s is attempting to extend %s, an unregistered element ' + |
| 2262 'or one that was not registered with Polymer.', this.name, |
| 2263 this.extends); |
| 2264 } |
| 2265 this.register(this.name, this.extends); |
| 2266 this.registered = true; |
| 2267 //console.groupEnd(); |
| 2268 }, |
| 2269 |
| 2270 waitingForPrototype: function(name) { |
| 2271 if (!getRegisteredPrototype(name)) { |
| 2272 // then wait for a prototype |
| 2273 waitingForPrototype(name, this); |
| 2274 // emulate script if user is not supplying one |
| 2275 this.handleNoScript(name); |
| 2276 // prototype not ready yet |
| 2277 return true; |
| 2278 } |
| 2279 }, |
| 2280 |
| 2281 handleNoScript: function(name) { |
| 2282 // if explicitly marked as 'noscript' |
| 2283 if (this.hasAttribute('noscript') && !this.noscript) { |
| 2284 this.noscript = true; |
| 2285 // TODO(sorvell): CustomElements polyfill awareness: |
| 2286 // noscript elements should upgrade in logical order |
| 2287 // script injection ensures this under native custom elements; |
| 2288 // under imports + ce polyfills, scripts run before upgrades. |
| 2289 // dependencies should be ready at upgrade time so register |
| 2290 // prototype at this time. |
| 2291 if (window.CustomElements && !CustomElements.useNative) { |
| 2292 Polymer(name); |
| 2293 } else { |
| 2294 var script = document.createElement('script'); |
| 2295 script.textContent = 'Polymer(\'' + name + '\');'; |
| 2296 this.appendChild(script); |
| 2297 } |
| 2298 } |
| 2299 }, |
| 2300 |
| 2301 waitingForResources: function() { |
| 2302 return this._needsResources; |
| 2303 }, |
| 2304 |
| 2305 // NOTE: Elements must be queued in proper order for inheritance/composition |
| 2306 // dependency resolution. Previously this was enforced for inheritance, |
| 2307 // and by rule for composition. It's now entirely by rule. |
| 2308 waitingForQueue: function() { |
| 2309 return queue.wait(this, this.registerWhenReady, this._register); |
| 2310 }, |
| 2311 |
| 2312 loadResources: function() { |
| 2313 this._needsResources = true; |
| 2314 this.loadStyles(function() { |
| 2315 this._needsResources = false; |
| 2316 this.registerWhenReady(); |
| 2317 }.bind(this)); |
| 2318 } |
| 2319 |
| 2320 }); |
| 2321 |
| 2322 // semi-pluggable APIs |
| 2323 |
| 2324 // TODO(sjmiles): should be fully pluggable (aka decoupled, currently |
| 2325 // the various plugins are allowed to depend on each other directly) |
| 2326 api.publish(api.declaration, prototype); |
| 2327 |
| 2328 // utility and bookkeeping |
| 2329 |
| 2330 function isRegistered(name) { |
| 2331 return Boolean(HTMLElement.getPrototypeForTag(name)); |
| 2332 } |
| 2333 |
| 2334 function isCustomTag(name) { |
| 2335 return (name && name.indexOf('-') >= 0); |
| 2336 } |
| 2337 |
| 2338 // exports |
| 2339 |
| 2340 scope.getRegisteredPrototype = getRegisteredPrototype; |
| 2341 |
| 2342 // boot tasks |
| 2343 |
| 2344 whenPolymerReady(function() { |
| 2345 document.body.removeAttribute('unresolved'); |
| 2346 document.dispatchEvent( |
| 2347 new CustomEvent('polymer-ready', {bubbles: true}) |
| 2348 ); |
| 2349 }); |
| 2350 |
| 2351 // register polymer-element with document |
| 2352 |
| 2353 document.registerElement('polymer-element', {prototype: prototype}); |
| 2354 |
| 2355 })(Polymer); |
| 2356 |
| 2357 //# sourceMappingURL=polymer.concat.js.map |
OLD | NEW |