Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: static/js/animation.js

Issue 4661048523096064: Issue 2120 - Add support for animations. (Closed)
Patch Set: Forgot to give localise_path filter a default return! Created April 9, 2015, 8:44 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 if (typeof window.addEventListener != "undefined") {
Sebastian Noack 2015/04/10 07:55:46 Is this our code? If yo the copyright and license
kzar 2015/04/11 13:32:01 We got to the bottom of it in the end. Done.
2 window.addEventListener("load", initAnimations, false);
3 document.addEventListener("click", loadAnimation, false);
4 }
5
6 var ns = "https://adblockplus.org/animation";
7 var loadPrefix = "animations/";
8 var loadSuffix = ".xml";
9
10 function initAnimations() {
11 var list = document.getElementsByTagNameNS(ns, "animation");
12 if (list.length == 0)
13 list = document.getElementsByTagName("animation"); // HTML mode, Anwiki
14 for (var i = 0; i < list.length; i++) {
15 var node = list[i];
16 if (!node.hasAttribute("name") || !node.hasAttribute("label"))
17 continue;
18
19 var animation = document.createElement("div");
20 animation.setAttribute("class", "animation");
21 animation.setAttribute("name", node.getAttribute("name"));
22 animation.setAttribute("started", false);
23 animation.style.margin = "5px";
24
25 var link = document.createElement("a");
26 link.appendChild(document.createTextNode(node.getAttribute("label")));
27 animation.appendChild(link);
28
29 node.parentNode.insertBefore(animation, node);
30 }
31 }
32
33 function loadAnimation(event) {
34 var animation = event.target;
35 while (animation && animation.nodeType == 1 && animation.className != "animati on")
36 animation = animation.parentNode;
37
38 if (!animation || animation.nodeType != 1 || animation.getAttribute("started") == "true")
39 return;
40
41 animation.setAttribute("started", "true");
42
43 while (animation.firstChild)
44 animation.removeChild(animation.firstChild);
45
46 animation.style.border = "1px dashed black";
47 animation.style.padding = "10px";
48 animation.style.width = "50px";
49 animation.style.height = "50px";
50
51 var container = document.createElement("div");
52 container.style.width = "0px";
53 container.style.height = "0px";
54 container.style.overflow = "visible";
55 container.style.position = "relative";
56 container.style.visibility = "hidden";
57
58 var image = document.createElement("img");
59 container.appendChild(image);
60 animation.appendChild(container);
61
62 image.onload = function() {
63 container.style.left = Math.round((50 - image.width) / 2) + "px";
64 container.style.top = Math.round((50 - image.height) / 2) + "px";
65 container.style.visibility = "";
66 }
67 image.src = "data:image/gif;base64,R0lGODlhGAAYAPQAAP%2F%2F%2FwCA%2F87m%2Fvr8% 2FuDv%2FrDX%2Fujz%2Fo7G%2Fsjj%2FpzN%2Ftjr%2FqjT%2FsDf%2FvL4%2Fna6%2FobC%2Frjb%2F miz%2FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH%2BGkNyZWF0ZWQgd 2l0aCBhamF4bG9hZC5pbmZvACH5BAAHAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAGAAYAAAFriAgj iQAQWVaDgr5POSgkoTDjFE0NoQ8iw8HQZQTDQjDn4jhSABhAAOhoTqSDg7qSUQwxEaEwwFhXHhHgzOA1 xshxAnfTzotGRaHglJqkJcaVEqCgyoCBQkJBQKDDXQGDYaIioyOgYSXA36XIgYMBWRzXZoKBQUMmil0l galLSIClgBpO0g%2Bs26nUWddXyoEDIsACq5SsTMMDIECwUdJPw0Mzsu0qHYkw72bBmozIQAh%2BQQAB wABACwAAAAAGAAYAAAFsCAgjiTAMGVaDgR5HKQwqKNxIKPjjFCk0KNXC6ATKSI7oAhxWIhezwhENTCQE oeGCdWIPEgzESGxEIgGBWstEW4QCGGAIJEoxGmGt5ZkgCRQQHkGd2CESoeIIwoMBQUMP4cNeQQGDYuNj 4iSb5WJnmeGng0CDGaBlIQEJziHk3sABidDAHBgagButSKvAAoyuHuUYHgCkAZqebw0AgLBQyyzNKO3b yNuoSS8x8OfwIchACH5BAAHAAIALAAAAAAYABgAAAW4ICCOJIAgZVoOBJkkpDKoo5EI43GMjNPSokXCI NKJCI4HcCRIQEQvqIOhGhBHhUTDhGo4diOZyFAoKEQDxra2mAEgjghOpCgz3LTBIxJ5kgwMBShACREHZ 1V4Kg1rS44pBAgMDAg%2FSw0GBAQGDZGTlY%2BYmpyPpSQDiqYiDQoCliqZBqkGAgKIS5kEjQ21VwCyp 76dBHiNvz%2BMR74AqSOdVwbQuo%2Babppo10ssjdkAnc0rf8vgl8YqIQAh%2BQQABwADACwAAAAAGAA YAAAFrCAgjiQgCGVaDgZZFCQxqKNRKGOSjMjR0qLXTyciHA7AkaLACMIAiwOC1iAxCrMToHHYjWQiA4N BEA0Q1RpWxHg4cMXxNDk4OBxNUkPAQAEXDgllKgMzQA1pSYopBgonCj9JEA8REQ8QjY%2BRQJOVl4ugo YssBJuMpYYjDQSliwasiQOwNakALKqsqbWvIohFm7V6rQAGP6%2BJQLlFg7KDQLKJrLjBKbvAor3IKiE AIfkEAAcABAAsAAAAABgAGAAABbUgII4koChlmhokw5DEoI4NQ4xFMQoJO4uuhignMiQWvxGBIQC%2BA JBEUyUcIRiyE6CR0CllW4HABxBURTUw4nC4FcWo5CDBRpQaCoF7VjgsyCUDYDMNZ0mHdwYEBAaGMwwHD g4HDA2KjI4qkJKUiJ6faJkiA4qAKQkRB3E0i6YpAw8RERAjA4tnBoMApCMQDhFTuySKoSKMJAq6rD4Gz ASiJYtgi6PUcs9Kew0xh7rNJMqIhYchACH5BAAHAAUALAAAAAAYABgAAAW0ICCOJEAQZZo2JIKQxqCOj WCMDDMqxT2LAgELkBMZCoXfyCBQiFwiRsGpku0EshNgUNAtrYPT0GQVNRBWwSKBMp98P24iISgNDAS4i pGA6JUpA2WAhDR4eWM%2FCAkHBwkIDYcGiTOLjY%2BFmZkNlCN3eUoLDmwlDW%2BAAwcODl5bYl8wCVY MDw5UWzBtnAANEQ8kBIM0oAAGPgcREIQnVloAChEOqARjzgAQEbczg8YkWJq8nSUhACH5BAAHAAYALAA AAAAYABgAAAWtICCOJGAYZZoOpKKQqDoORDMKwkgwtiwSBBYAJ2owGL5RgxBziQQMgkwoMkhNqAEDARP SaiMDFdDIiRSFQowMXE8Z6RdpYHWnEAWGPVkajPmARVZMPUkCBQkJBQINgwaFPoeJi4GVlQ2Qc3VJBQc LV0ptfAMJBwdcIl%2BFYjALQgimoGNWIhAQZA4HXSpLMQ8PIgkOSHxAQhERPw7ASTSFyCMMDqBTJL8tf 3y2fCEAIfkEAAcABwAsAAAAABgAGAAABa8gII4k0DRlmg6kYZCoOg5EDBDEaAi2jLO3nEkgkMEIL4BLp BAkVy3hCTAQKGAznM0AFNFGBAbj2cA9jQixcGZAGgECBu%2F9HnTp%2BFGjjezJFAwFBQwKe2Z%2BKoC ChHmNjVMqA21nKQwJEJRlbnUFCQlFXlpeCWcGBUACCwlrdw8RKGImBwktdyMQEQciB7oACwcIeA4RVwA ODiIGvHQKERAjxyMIB5QlVSTLYLZ0sW8hACH5BAAHAAgALAAAAAAYABgAAAW0ICCOJNA0ZZoOpGGQrDo OBCoSxNgQsQzgMZyIlvOJdi%2BAS2SoyXrK4umWPM5wNiV0UDUIBNkdoepTfMkA7thIECiyRtUAGq8fm 2O4jIBgMBA1eAZ6Knx%2BgHaJR4QwdCMKBxEJRggFDGgQEREPjjAMBQUKIwIRDhBDC2QNDDEKoEkDoiM HDigICGkJBS2dDA6TAAnAEAkCdQ8ORQcHTAkLcQQODLPMIgIJaCWxJMIkPIoAt3EhACH5BAAHAAkALAA AAAAYABgAAAWtICCOJNA0ZZoOpGGQrDoOBCoSxNgQsQzgMZyIlvOJdi%2BAS2SoyXrK4umWHM5wNiV0U N3xdLiqr%2BmENcWpM9TIbrsBkEck8oC0DQqBQGGIz%2Bt3eXtob0ZTPgNrIwQJDgtGAgwCWSIMDg4Hi iUIDAxFAAoODwxDBWINCEGdSTQkCQcoegADBaQ6MggHjwAFBZUFCm0HB0kJCUy9bAYHCCPGIwqmRq0jy SMGmj6yRiEAIfkEAAcACgAsAAAAABgAGAAABbIgII4k0DRlmg6kYZCsOg4EKhLE2BCxDOAxnIiW84l2L 4BLZKipBopW8XRLDkeCiAMyMvQAA%2BuON4JEIo%2BvqukkKQ6RhLHplVGN%2BLyKcXA4Dgx5DWwGDXx %2BgIKENnqNdzIDaiMECwcFRgQCCowiCAcHCZIlCgICVgSfCEMMnA0CXaU2YSQFoQAKUQMMqjoyAglcA AyBAAIMRUYLCUkFlybDeAYJryLNk6xGNCTQXY0juHghACH5BAAHAAsALAAAAAAYABgAAAWzICCOJNA0Z VoOAmkY5KCSSgSNBDE2hDyLjohClBMNij8RJHIQvZwEVOpIekRQJyJs5AMoHA%2BGMbE1lnm9EcPhOHR nhpwUl3AsknHDm5RN%2Bv8qCAkHBwkIfw1xBAYNgoSGiIqMgJQifZUjBhAJYj95ewIJCQV7KYpzBAkLL QADCHOtOpY5PgNlAAykAEUsQ1wzCgWdCIdeArczBQVbDJ0NAqyeBb64nQAGArBTt8R8mLuyPyEAOwAAA AAAAAAAAA%3D%3D";
68
69 var request = new XMLHttpRequest();
70 request.open("GET", loadPrefix + encodeURIComponent(animation.getAttribute("na me")) + loadSuffix);
71 request.onload = function() {
72 if (!request.responseXML || !request.responseXML.documentElement)
73 return;
74
75 animation._data = request.responseXML.documentElement;
76 initAnimation(animation);
77 }
78 request.send(null);
79 }
80
81 function initAnimation(animation) {
82 while (animation.firstChild)
83 animation.removeChild(animation.firstChild);
84
85 var data = animation._data;
86 animation._width = (parseInt(data.getAttribute("width")) || 150);
87 animation._height = (parseInt(data.getAttribute("height")) || 150);
88 animation._stepSize = parseInt(data.getAttribute("step")) || 20;
89
90 animation.style.width = animation._width + "px";
91 animation.style.height = animation._height + "px";
92
93 var count = 0;
94 var initializer = function(event) {
95 count--;
96 if (count <= 0)
97 postInitAnimation(animation);
98 }
99
100 animation._objects = {};
101 var list = data.getElementsByTagNameNS(ns, "object");
102 for (var i = 0; i < list.length; i++) {
103 var node = list[i];
104 if (!node.hasAttribute("id") || !node.hasAttribute("src"))
105 continue;
106
107 count++;
108
109 var image = document.createElement("img");
110 image.addEventListener("load", initializer, false);
111 image.src = node.getAttribute("src");
112
113 var object = document.createElement("div");
114 object.style.width = "0px";
115 object.style.height = "0px";
116 object.style.overflow = "visible";
117 object.style.position = "relative";
118 object.style.visibility = "hidden";
119 object._anchor = node.getAttribute("anchor");
120 object._coords = [0,0];
121
122 object.appendChild(image);
123 animation.insertBefore(object, animation.firstChild);
124 animation._objects[node.getAttribute("id")] = object;
125 }
126 }
127
128 function postInitAnimation(animation) {
129 for (var key in animation._objects) {
130 var object = animation._objects[key];
131 var image = object.firstChild;
132
133 object._size = [image.width, image.height];
134 var offset = parseCoords(object._anchor);
135 if (offset) {
136 offset[0] = -offset[0];
137 offset[1] = -offset[1];
138 }
139 else {
140 offset = [0, 0];
141 if (object._anchor)
142 processAnchor(offset, object._anchor, [-image.width, -image.height]);
143 }
144 object._offset = offset;
145 }
146
147 animation._actions = [];
148
149 var getStepData = function(step) {
150 if (typeof animation._actions[step] == "undefined")
151 animation._actions[step] = [];
152
153 return animation._actions[step];
154 }
155
156 var list = animation._data.getElementsByTagNameNS(ns, "action");
157 var timestamp = 0;
158 for (var i = 0; i < list.length; i++) {
159 var node = list[i];
160 var prevTimestamp = timestamp;
161 timestamp += (parseInt(node.getAttribute("delay")) || 0);
162
163 // Update position for moving objects
164 for (var t = prevTimestamp + 1; t < timestamp; t++) {
165 if (typeof animation._actions[t] != "undefined") {
166 var curActions = animation._actions[t];
167 for (var j = 0; j < curActions.length; j++)
168 if (typeof curActions[j].obj != "undefined")
169 curActions[j].obj._coords = curActions[j].coords;
170 }
171 }
172
173 for (var child = node.firstChild; child; child = child.nextSibling) {
174 if (child.nodeType != 1)
175 continue;
176
177 var objects = child.getAttribute("object");
178 if (objects) {
179 objects = objects.split(/\s*,\s*/);
180 for (var j = 0; j < objects.length; j++) {
181 if (typeof animation._objects[objects[j]] == "undefined")
182 objects.splice(j--, 1);
183 else
184 objects[j] = animation._objects[objects[j]];
185 }
186 }
187 if (objects.length == 0)
188 continue;
189
190 if (child.localName == "show") {
191 var coords = parseRelativeCoords(animation._objects, child, "coords", "c oordsOf", "anchor", "offset");
192 if (coords) {
193 var data = getStepData(timestamp);
194 for (var j = 0; j < objects.length; j++) {
195 objects[j]._coords = coords;
196 data.push({obj: objects[j], coords: coords});
197 }
198 }
199 }
200 else if (child.localName == "hide" || child.localName == "replace") {
201 var data = getStepData(timestamp);
202 data.push.apply(getStepData(timestamp), objects);
203 if (child.localName == "replace" && typeof animation._objects[child.getA ttribute("with")] != "undefined") {
204 var object = animation._objects[child.getAttribute("with")];
205 object._coords = objects[0]._coords;
206 data.push({obj: object, coords: object._coords});
207 }
208 }
209 else if (child.localName == "move") {
210 var from = parseRelativeCoords(animation._objects, child, "fromCoords", "fromCoordsOf", "fromAnchor", "fromOffset");
211 var to = parseRelativeCoords(animation._objects, child, "toCoords", "toC oordsOf", "toAnchor", "toOffset");
212 var duration = parseInt(child.getAttribute("duration")) || 0;
213 if (to) {
214 if (!from)
215 from = objects[0]._coords;
216
217 for (var j = 0; j < objects.length; j++)
218 objects[j]._coords = from;
219
220 for (var t = timestamp; t <= timestamp + duration; t++) {
221 var curCoords = [
222 (timestamp + duration - t) / duration * from[0] + (t - timestamp) / duration * to[0],
223 (timestamp + duration - t) / duration * from[1] + (t - timestamp) / duration * to[1]
224 ];
225 var data = getStepData(t);
226 for (var j = 0; j < objects.length; j++)
227 data.push({obj: objects[j], coords: curCoords});
228 }
229 }
230 }
231 }
232 }
233
234 runAnimation(animation);
235 }
236
237 function executeAnimationStep(animation, timestamp) {
238 if (typeof animation._actions[timestamp] == "undefined")
239 return;
240
241 var action = animation._actions[timestamp];
242 for (var i = 0; i < action.length; i++) {
243 if (action[i] instanceof HTMLElement)
244 action[i].style.visibility = "hidden";
245 else {
246 action[i].obj.style.left = (action[i].coords[0] + action[i].obj._offset[0] ) + "px";
247 action[i].obj.style.top = (action[i].coords[1] + action[i].obj._offset[1]) + "px";
248 action[i].obj.style.visibility = "";
249 }
250 }
251 }
252
253 function runAnimation(animation) {
254 if (typeof animation.scrollIntoView == "function")
255 animation.scrollIntoView(true)
256
257 executeAnimationStep(animation, 0);
258
259 var curStep = 0;
260 var interval = setInterval(function() {
261 executeAnimationStep(animation, ++curStep);
262 if (curStep >= animation._actions.length) {
263 clearInterval(interval);
264 animation.style.cursor = "pointer";
265 animation.addEventListener("click", restartAnimation, false);
266 }
267 }, animation._stepSize);
268 }
269
270 function restartAnimation(event) {
271 var animation = event.target;
272 while (animation && animation.nodeType == 1 && animation.className != "animati on")
273 animation = animation.parentNode;
274
275 if (!animation || animation.nodeType != 1)
276 return;
277
278 animation.removeEventListener("click", restartAnimation, false);
279 animation.style.cursor = "";
280
281 for (var key in animation._objects)
282 animation._objects[key].style.visibility = "hidden";
283
284 runAnimation(animation);
285 }
286
287 function parseCoords(coords) {
288 if (!/^\s*([\-+]?\d+)\s*,\s*([\-+]?\d+)\s*$/.test(coords))
289 return null;
290
291 return [parseInt(RegExp.$1) || 0, parseInt(RegExp.$2) || 0];
292 }
293
294 function parseRelativeCoords(objects, node, coordsAttr, objectAttr, anchorAttr, offsetAttr) {
295 var coords = parseCoords(node.getAttribute(coordsAttr));
296 if (!coords) {
297 var object = node.getAttribute(objectAttr);
298 if (object && typeof objects[object] != "undefined") {
299 object = objects[object];
300 coords = object._coords.slice(0);
301
302 var anchor = node.getAttribute(anchorAttr);
303 if (anchor) {
304 coords[0] += object._offset[0];
305 coords[1] += object._offset[1];
306 processAnchor(coords, anchor, object._size);
307 }
308 }
309 }
310
311 if (coords) {
312 var offset = parseCoords(node.getAttribute(offsetAttr));
313 if (offset) {
314 coords[0] += offset[0];
315 coords[1] += offset[1];
316 }
317 }
318
319 return coords;
320 }
321
322 function processAnchor(coords, anchor, size) {
323 if (/right/i.test(anchor))
324 coords[0] += size[0];
325 else if (!/left/i.test(anchor))
326 coords[0] += Math.round(size[0]/2);
327
328 if (/bottom/i.test(anchor))
329 coords[1] += size[1];
330 else if (!/top/i.test(anchor))
331 coords[1] += Math.round(size[1]/2);
332 }
OLDNEW

Powered by Google App Engine
This is Rietveld