UNPKG

5.13 kBJavaScriptView Raw
1"use strict";
2var utils = require('./utils');
3
4exports.property = function(attr) {
5 if (Array.isArray(attr.type)) {
6 var valid = Object.create(null);
7 attr.type.forEach(function(val) {
8 valid[val.value || val] = val.alias || val;
9 });
10 var missingValueDefault = attr.missing;
11 if (missingValueDefault===undefined) { missingValueDefault = null; }
12 var invalidValueDefault = attr.invalid;
13 if (invalidValueDefault===undefined) { invalidValueDefault = missingValueDefault; }
14 return {
15 get: function() {
16 var v = this._getattr(attr.name);
17 if (v === null) return missingValueDefault;
18
19 v = valid[v.toLowerCase()];
20 if (v !== undefined) return v;
21 if (invalidValueDefault !== null) return invalidValueDefault;
22 return v;
23 },
24 set: function(v) {
25 this._setattr(attr.name, v);
26 }
27 };
28 }
29 else if (attr.type === Boolean) {
30 return {
31 get: function() {
32 return this.hasAttribute(attr.name);
33 },
34 set: function(v) {
35 if (v) {
36 this._setattr(attr.name, '');
37 }
38 else {
39 this.removeAttribute(attr.name);
40 }
41 }
42 };
43 }
44 else if (attr.type === Number ||
45 attr.type === "long" ||
46 attr.type === "unsigned long" ||
47 attr.type === "limited unsigned long with fallback") {
48 return numberPropDesc(attr);
49 }
50 else if (!attr.type || attr.type === String) {
51 return {
52 get: function() { return this._getattr(attr.name) || ''; },
53 set: function(v) {
54 if (attr.treatNullAsEmptyString && v === null) { v = ''; }
55 this._setattr(attr.name, v);
56 }
57 };
58 }
59 else if (typeof attr.type === 'function') {
60 return attr.type(attr.name, attr);
61 }
62 throw new Error('Invalid attribute definition');
63};
64
65// See http://www.whatwg.org/specs/web-apps/current-work/#reflect
66//
67// defval is the default value. If it is a function, then that function
68// will be invoked as a method of the element to obtain the default.
69// If no default is specified for a given attribute, then the default
70// depends on the type of the attribute, but since this function handles
71// 4 integer cases, you must specify the default value in each call
72//
73// min and max define a valid range for getting the attribute.
74//
75// setmin defines a minimum value when setting. If the value is less
76// than that, then throw INDEX_SIZE_ERR.
77//
78// Conveniently, JavaScript's parseInt function appears to be
79// compatible with HTML's 'rules for parsing integers'
80function numberPropDesc(a) {
81 var def;
82 if(typeof a.default === 'function') {
83 def = a.default;
84 }
85 else if(typeof a.default === 'number') {
86 def = function() { return a.default; };
87 }
88 else {
89 def = function() { utils.assert(false, typeof a.default); };
90 }
91 var unsigned_long = (a.type === 'unsigned long');
92 var signed_long = (a.type === 'long');
93 var unsigned_fallback = (a.type === 'limited unsigned long with fallback');
94 var min = a.min, max = a.max, setmin = a.setmin;
95 if (min === undefined) {
96 if (unsigned_long) min = 0;
97 if (signed_long) min = -0x80000000;
98 if (unsigned_fallback) min = 1;
99 }
100 if (max === undefined) {
101 if (unsigned_long || signed_long || unsigned_fallback) max = 0x7FFFFFFF;
102 }
103
104 return {
105 get: function() {
106 var v = this._getattr(a.name);
107 var n = a.float ? parseFloat(v) : parseInt(v, 10);
108 if (v === null || !isFinite(n) || (min !== undefined && n < min) || (max !== undefined && n > max)) {
109 return def.call(this);
110 }
111 if (unsigned_long || signed_long || unsigned_fallback) {
112 if (!/^[ \t\n\f\r]*[-+]?[0-9]/.test(v)) { return def.call(this); }
113 n = n|0; // jshint ignore:line
114 }
115 return n;
116 },
117 set: function(v) {
118 if (!a.float) { v = Math.floor(v); }
119 if (setmin !== undefined && v < setmin) {
120 utils.IndexSizeError(a.name + ' set to ' + v);
121 }
122 if (unsigned_long) {
123 v = (v < 0 || v > 0x7FFFFFFF) ? def.call(this) :
124 (v|0); // jshint ignore:line
125 } else if (unsigned_fallback) {
126 v = (v < 1 || v > 0x7FFFFFFF) ? def.call(this) :
127 (v|0); // jshint ignore:line
128 } else if (signed_long) {
129 v = (v < -0x80000000 || v > 0x7FFFFFFF) ? def.call(this) :
130 (v|0); // jshint ignore:line
131 }
132 this._setattr(a.name, String(v));
133 }
134 };
135}
136
137// This is a utility function for setting up change handler functions
138// for attributes like 'id' that require special handling when they change.
139exports.registerChangeHandler = function(c, name, handler) {
140 var p = c.prototype;
141
142 // If p does not already have its own _attributeChangeHandlers
143 // then create one for it, inheriting from the inherited
144 // _attributeChangeHandlers. At the top (for the Element class) the
145 // _attributeChangeHandlers object will be created with a null prototype.
146 if (!Object.prototype.hasOwnProperty.call(p, '_attributeChangeHandlers')) {
147 p._attributeChangeHandlers =
148 Object.create(p._attributeChangeHandlers || null);
149 }
150
151 p._attributeChangeHandlers[name] = handler;
152};