1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178 | 22
22
22
22
22
22
22
22
8
2
22
456
456
456
376
376
376
117
76
76
117
117
80
294
294
10
284
172
439
112
307
66
22
22
22
22
22
66
66
66
66
2
1
1
22
33482
22
| define(function () {
// summary:
// This module defines miscellaneous utility methods for purposes of
// adding styles, and throttling/debouncing function calls.
// establish an extra stylesheet which addCssRule calls will use,
// plus an array to track actual indices in stylesheet for removal
var extraRules = [],
extraSheet,
removeMethod,
rulesProperty,
invalidCssChars = /([^A-Za-z0-9_\u00A0-\uFFFF-])/g;
function removeRule(index) {
// Function called by the remove method on objects returned by addCssRule.
var realIndex = extraRules[index],
i, l;
Iif (realIndex === undefined) {
return; // already removed
}
// remove rule indicated in internal array at index
extraSheet[removeMethod](realIndex);
// Clear internal array item representing rule that was just deleted.
// NOTE: we do NOT splice, since the point of this array is specifically
// to negotiate the splicing that occurs in the stylesheet itself!
extraRules[index] = undefined;
// Then update array items as necessary to downshift remaining rule indices.
// Can start at index + 1, since array is sparse but strictly increasing.
for (i = index + 1, l = extraRules.length; i < l; i++) {
if (extraRules[i] > realIndex) {
extraRules[i]--;
}
}
}
var util = {
// Throttle/debounce functions
defaultDelay: 15,
throttle: function (cb, context, delay) {
// summary:
// Returns a function which calls the given callback at most once per
// delay milliseconds. (Inspired by plugd)
var ran = false;
delay = delay || util.defaultDelay;
return function () {
if (ran) {
return;
}
ran = true;
cb.apply(context, arguments);
setTimeout(function () {
ran = false;
}, delay);
};
},
throttleDelayed: function (cb, context, delay) {
// summary:
// Like throttle, except that the callback runs after the delay,
// rather than before it.
var ran = false;
delay = delay || util.defaultDelay;
return function () {
if (ran) {
return;
}
ran = true;
var a = arguments;
setTimeout(function () {
ran = false;
cb.apply(context, a);
}, delay);
};
},
debounce: function (cb, context, delay) {
// summary:
// Returns a function which calls the given callback only after a
// certain time has passed without successive calls. (Inspired by plugd)
var timer;
delay = delay || util.defaultDelay;
return function () {
if (timer) {
clearTimeout(timer);
timer = null;
}
var a = arguments;
timer = setTimeout(function () {
cb.apply(context, a);
}, delay);
};
},
// Iterative functions
each: function (arrayOrObject, callback, context) {
// summary:
// Given an array or object, iterates through its keys.
// Does not use hasOwnProperty (since even Dojo does not
// consistently use it), but will iterate using a for or for-in
// loop as appropriate.
var i, len;
if (!arrayOrObject) {
return;
}
if (typeof arrayOrObject.length === 'number') {
for (i = 0, len = arrayOrObject.length; i < len; i++) {
callback.call(context, arrayOrObject[i], i, arrayOrObject);
}
}
else {
for (i in arrayOrObject) {
callback.call(context, arrayOrObject[i], i, arrayOrObject);
}
}
},
// CSS-related functions
addCssRule: function (selector, css) {
// summary:
// Dynamically adds a style rule to the document. Returns an object
// with a remove method which can be called to later remove the rule.
if (!extraSheet) {
// First time, create an extra stylesheet for adding rules
extraSheet = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(extraSheet);
// Keep reference to actual StyleSheet object (`styleSheet` for IE < 9)
extraSheet = extraSheet.sheet || extraSheet.styleSheet;
// Store name of method used to remove rules (`removeRule` for IE < 9)
removeMethod = extraSheet.deleteRule ? 'deleteRule' : 'removeRule';
// Store name of property used to access rules (`rules` for IE < 9)
rulesProperty = extraSheet.cssRules ? 'cssRules' : 'rules';
}
var index = extraRules.length;
extraRules[index] = (extraSheet.cssRules || extraSheet.rules).length;
extraSheet.addRule ?
extraSheet.addRule(selector, css) :
extraSheet.insertRule(selector + '{' + css + '}', extraRules[index]);
return {
get: function (prop) {
return extraSheet[rulesProperty][extraRules[index]].style[prop];
},
set: function (prop, value) {
Eif (typeof extraRules[index] !== 'undefined') {
extraSheet[rulesProperty][extraRules[index]].style[prop] = value;
}
},
remove: function () {
removeRule(index);
}
};
},
escapeCssIdentifier: function (id, replace) {
// summary:
// Escapes normally-invalid characters in a CSS identifier (such as . or :);
// see http://www.w3.org/TR/CSS2/syndata.html#value-def-identifier
// id: String
// CSS identifier (e.g. tag name, class, or id) to be escaped
// replace: String?
// If specified, indicates that invalid characters should be
// replaced by the given string rather than being escaped
return typeof id === 'string' ? id.replace(invalidCssChars, replace || '\\$1') : id;
}
};
return util;
});
|