forked from kriszyp/xstyle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
elemental.js
158 lines (155 loc) · 5.75 KB
/
elemental.js
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
/* Provides DOM element level interaction for CSS extensions */
define([], function(){
var testDiv = document.createElement("div");
var features = {
"dom-qsa2.1": !!testDiv.querySelectorAll
};
function has(feature){
return features[feature];
}
var matchesSelector = testDiv.matchesSelector || testDiv.webkitMatchesSelector || testDiv.mozMatchesSelector || testDiv.msMatchesSelector || testDiv.oMatchesSelector;
var selectorRenderers = [];
var classHash = {}, propertyHash = {};
var renderQueue = [];
var documentQueried;
require(["dojo/domReady!"], function(){
documentQueried = true;
if(has("dom-qsa2.1")){
for(var i = 0, l = selectorRenderers.length; i < l; i++){
findMatches(selectorRenderers[i]);
}
renderWaiting();
}else{
var all = document.all;
for(var i = 0, l = all.length; i < l; i++){
update(all[i]);
}
}
});//else rely on css expressions (or maybe we should use document.all and just scan everything)
function findMatches(renderer){
// find the elements for a given selector and apply the renderers to it
var toRender = [];
var results = document.querySelectorAll(renderer.selector);
var name = renderer.name;
for(var i = 0, l = results.length; i < l; i++){
var element = results[i];
var currentStyle = element.elementalStyle;
var currentSpecificities = element.elementalSpecificities;
if(!currentStyle){
currentStyle = element.elementalStyle = {};
currentSpecificities = element.elementalSpecificities = {};
}
// TODO: only override if the selector is equal or higher specificity
// var specificity = renderer.selector.match(/ /).length;
if(true || currentSpecificities[name] <= renderer.specificity){ // only process changes
var elementRenderings = element.renderings;
if(!elementRenderings){
elementRenderings = element.renderings = [];
renderQueue.push(element);
}
elementRenderings.push({
name: name,
rendered: currentStyle[name] == renderer.propertyValue,
renderer: renderer
});
currentStyle[name] = renderer.propertyValue;
}
}
}
function renderWaiting(){
// render all the elements in the queue to be rendered
for(var i = 0; i < renderQueue.length; i++){
var element = renderQueue[i];
var renderings = element.renderings, currentStyle = element.elementalStyle;
for(var j = 0; j < renderings.length; j++){
var rendering = renderings[j];
var renderer = rendering.renderer;
var rendered = renderer.rendered;
isCurrent = currentStyle[rendering.name] == renderer.propertyValue; // determine if this renderer matches the current computed style
if(!rendered && isCurrent){
renderer.render(element);
}
if(rendered && !isCurrent && renderer.unrender){
renderer.unrender(element);
renderings.splice(j--, 1); // TODO: need to remove duplicate rendered items as well
}
}
}
renderQueue = [];
}
function apply(element, renderers){
// an element was found that matches a selector, so we apply the renderers
for(var i = 0, l = renderers.length; i < l; i++){
renderers[i](element);
}
}
put = typeof put == "undefined" ? {} : put;
put.onaddclass = function(element, className){
var selectorRenderers = classTriggers[className];
var renderers = selectorRenderers[selector];
for(var i = 0, l = selectorRenderers.length; i < l; i++){
var renderer = selectorRenderers[i];
if(matchesSelector.apply(element, renderer.selector)){
renderer.render(element);
(element.renderers = element.renderers || []).push(renderer);
}
}
};
put.onremoveclass = function(element){
var elementRenderers = element.renderers;
if(elementRenderers){
for(var i = elementRenderers.length - 1; i >= 0; i--){
var renderer = elementRenderers[i];
if(!matchesSelector.apply(element, renderer.selector)){
renderer.unrender(element);
elementRenderers.splice(i, 1);
}
}
}
};
put.oncreateelement = function(element){
tagTriggers[element.tagName]
}
function update(element){
/* At some point, might want to use getMatchedCSSRules for faster access to matching rules
if(typeof getMatchedCSSRules != "undefined"){
// webkit gives us fast access to which rules apply
getMatchedCSSRules(element);
}else{*/
for(var i = 0, l = selectorRenderers.length; i < l; i++){
var renderer = selectorRenderers[i];
if(matchesSelector ?
// use matchesSelector if available
matchesSelector.apply(element, renderer.selector) : // TODO: determine if it is higher specificity that other same name properties
// else use IE's custom css property inheritance mechanism
element.currentStyle[renderer.name] == renderer.propertyValue){
renderer.render(element);
}
}
}
return {
addRenderer: function(propertyName, propertyValue, rule, handler){
var renderer = {
selector: rule.selector,
propertyValue: propertyValue,
name: propertyName,
render: handler
};
// the main entry point for adding elemental handlers for a selector. The handler
// will be called for each element that is created that matches a given selector
selectorRenderers.push(renderer);
if(documentQueried){
findMatches(renderer);
}
renderWaiting();
/*if(!matchesSelector){
// create a custom property to identify this rule in created elements
return (renderers.triggerProperty = 'selector_' + encodeURIComponent(selector).replace(/%/g, '/')) + ': 1;' +
(document.querySelectorAll ? '' :
// we use css expressions for IE6-7 to find new elements that match the selector, since qSA is not available, wonder if it is better to just use document.all...
'zoom: expression(cssxRegister(this,"' + selector +'"));');
}*/
},
update: update // this should be called for newly created dynamic elements to ensure the proper rules are applied
};
});