source: other-projects/FileTransfer-WebSocketPair/Themes/themebuilder/bin/widgetdatasample/sliceme/screenshot-runner.js@ 31525

Last change on this file since 31525 was 31525, checked in by ak19, 7 years ago

Nathan provided more stuff: Themes folder contains Sencha's Themebuilder which generates GXT Themes. It includes the .theme and generated .jar files for the project theme.

File size: 11.7 KB
Line 
1/**
2 * This is a PhantomJS script that renders a running page and captures both a screenshot
3 * of the rendered page and a data snapshot of the ComponentManager's set of rendered
4 * widgets. This data is then used to extract theme images.
5 */
6var page = require('webpage').create(),
7 system = require('system'),
8 fs = require('fs');
9
10/**
11 * error handler logic, updates should be below this section
12 */
13page.onConsoleMessage = function (msg) {
14 console.log(msg);
15};
16
17function handleError (err, stack) {
18 console.log("== Unhandled Error ==");
19 phantom.defaultErrorHandler(err, stack);
20 phantom.exit(2);
21}
22
23page.onError = phantom.onError = handleError;
24
25/* end error handler setup */
26
27if (system.args.length < 2) {
28 console.log("usage:");
29 console.log(" <path to html file> <image name> <widget data file>]:");
30 phantom.exit(1);
31}
32
33/**
34 * args:
35 * 0 => this script's file name
36 * 1 => the html file to render (on windows, be mindful of '\\' chars)
37 * 2 => the name of the screen shot image (default: screenshot.png)
38 * 3 => the name of the widget data file (default: widgetdata.json)
39 */
40var url = system.args[1].replace("\\", "/"),
41 screenCapFileName = system.args[2],
42 widgetDataFile = system.args[3],
43 configFiles = Array.prototype.slice.call(system.args, [4]);
44
45
46console.log("loading page " + url);
47
48function waitFor (test, ready, timeout) {
49 var maxtimeOutMillis = timeout ? timeout : 30 * 1000,
50 start = new Date().getTime(),
51 condition = false,
52 interval = setInterval(function() {
53 if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
54 condition = test();
55 } else {
56 if (!condition) {
57 console.log('failed to render widgets within 30 sec.');
58 phantom.exit(1);
59 } else {
60 clearInterval(interval);
61 ready();
62 }
63 }
64 }, 100);
65}
66
67page.open(url, function (status) {
68 if (status === 'success') {
69 page.evaluate(function() {
70 if (document.addEventListener) {
71 document.addEventListener('DOMContentLoaded', function () {
72 // This is very important for getting transparency on corners.
73 document.body.style.backgroundColor = 'transparent';
74 });
75 }
76 document.body.style.backgroundColor = 'transparent';
77
78
79 window.generateSlicerManifest = function() {
80 var elements = document.body.querySelectorAll('.x-slicer-target');
81 var widgets = [];
82 var slicesRe = /^'x-slicer\:(.+)'$/;
83 var transparentRe = /^rgba\(.*[,]\s*0\)$/i;
84 var urlRe = /url[(]([^)]+)[)]/;
85
86 function getData (el) {
87 var data = el.getAttribute('data-slicer');
88 if (data) {
89 return JSON.parse(data);
90 }
91 return null;
92 }
93
94 function getSlices (entry, src) {
95 var content = src && src.content;
96 var slices = entry.slices;
97 if (content) {
98 var m = slicesRe.exec(content);
99 if (m && m[1]) {
100 var sliceStrings = m[1].split(', ');
101 forEach(sliceStrings, function(str){
102 // Each string looks like a url, with a schema, followed by some 'other' string, either a path or
103 // some other token
104 var colon = str.indexOf(':');
105 if (colon == -1) return;
106 var schema = str.slice(0, colon);
107 var path = str.slice(colon + 1);
108 if (schema == "stretch") {
109 // The stretch property is used to modify other slices for this widget, store it on its own
110 entry.stretch = path;
111 } else {
112 // The path indicates the desired output file to create for this type of slice operation
113 if (!!slices[schema] && 'url(' + slices[schema] + ')' != path) {
114 err("The widget " + entry.id + " declares two " + schema + " with two different urls");
115 }
116 // From SASS, this path is in the form of url(path), whereas we only want to pass along the inner
117 // part of the path
118 var urlMatch = urlRe.exec(path);
119 if (urlMatch && urlMatch[1]) {
120 slices[schema.replace(/-/g,'_')] = urlMatch[1];
121 } else {
122 err("The widget " + entry.id + "'s " + schema + " slice's url cannot be parsed: " + path);
123 }
124 }
125 });
126 }
127 }
128 }
129
130 function err (str) {
131 console.error(str);
132 throw str;
133 }
134
135 function forEach (it, fn) {
136 for (var i = 0; i < it.length; ++i) {
137 fn(it[i]);
138 }
139 }
140
141 function copyProps (dest, src) {
142 var out = dest || {};
143 if (!!src) {
144 for (var key in src) {
145 var val = src[key];
146 if (typeof(val) == "object") {
147 out[key] = copyProps(out[key], val);
148 } else {
149 out[key] = val;
150 }
151 }
152 }
153
154 return out;
155 }
156
157 forEach(elements, function (el) {
158 var view = el.ownerDocument.defaultView;
159 var style = view.getComputedStyle(el, null);
160 var bg = style['background-image'];
161 var box = el.getBoundingClientRect();
162
163 var entry = {
164 box: {
165 x: window.scrollX + box.left,
166 y: window.scrollY + box.top,
167 w: box.right - box.left,
168 h: box.bottom - box.top
169 },
170 radius: {
171 tl: parseInt(style['border-top-left-radius'], 10) || 0,
172 tr: parseInt(style['border-top-right-radius'], 10) || 0,
173 br: parseInt(style['border-bottom-right-radius'], 10) || 0,
174 bl: parseInt(style['border-bottom-left-radius'], 10) || 0
175 },
176 border: {
177 t: parseInt(style['border-top-width'], 10) || 0,
178 r: parseInt(style['border-right-width'], 10) || 0,
179 b: parseInt(style['border-bottom-width'], 10) || 0,
180 l: parseInt(style['border-left-width'], 10) || 0
181 }
182 };
183
184 if (bg.indexOf('-gradient') !== -1) {
185 if (bg.indexOf('50% 0') !== -1 || bg.indexOf('top') !== -1 ||
186 bg.indexOf('bottom') !== -1) {
187 entry.gradient = 'top';
188 } else {
189 entry.gradient = 'left';
190 }
191 }
192
193 // Reads from sass to get data
194 entry.slices = {};
195 getSlices(entry, view.getComputedStyle(el, ':after'));
196
197 if (!!el.id) {
198 entry.id = el.id;
199
200 // Merge with existing properties in global widgetSlices array, favoring widgetSlices
201 if (!!window.widgetSlices) {
202 entry = copyProps((window.widgetSlices && window.widgetSlices[el.id]), entry);
203 delete window.widgetSlices[el.id];
204 }
205 }
206
207 if (!entry.gradient && !entry.slices.length &&
208 transparentRe.test(style['background-color']) &&
209 transparentRe.test(style['border-top-color']) &&
210 transparentRe.test(style['border-right-color']) &&
211 transparentRe.test(style['border-bottom-color']) &&
212 transparentRe.test(style['border-left-color'])) {
213 // If we have no gradient and the background and border are both
214 // transparent, the Sass is allowed to have no slices. If we do
215 // the push on this entry, the slicer core will generate warnings
216 // about it. This is a Good Thing and we don't want to blindly
217 // mask legitimate issues such as not sending in the proper slice
218 // requests.
219 return;
220 }
221
222 widgets.push(entry);
223 });
224
225 if (!!window.widgetSlices) {
226 for (var id in window.widgetSlices) {
227// widgets.push(window.widgetSlices[id]);
228 console.error("Widget Slice detected without corresponding element : " + id);
229 }
230 }
231
232 var slicerManifest = window.slicerManifest = getData(document.body) || {};
233 slicerManifest.widgets = widgets;
234 if (!slicerManifest.format) {
235 // legacy support sets format to "1.0"
236 slicerManifest.format = '2.0';
237 }
238 window['widgetsReady'] = true;
239 return slicerManifest;
240 };
241
242 });
243
244 waitFor(function(){
245 return page.evaluate(function() {
246 return !!(window['readyForConfig']);
247 });
248 }, function() {
249 var cfgs = [];
250 for (var i = 0; i < configFiles.length; i++) {
251 cfgs.push(fs.read(configFiles[i]));
252 };
253 page.evaluate(function(cfgs) {
254 window.readyForConfig.apply(null, cfgs);
255 }, cfgs);
256 });
257
258 waitFor(function() {
259 return page.evaluate(function() {
260 return !!(window['widgetsReady']);
261 });
262 }, function() {
263 try {
264 console.log('Capturing screenshot');
265 page.render(screenCapFileName);
266 data = page.evaluate(function() {
267 if (!window["slicerManifest"]) {
268 window.generateSlicerManifest()
269 }
270 return window.slicerManifest;
271 });
272
273 if (data) {
274 console.log('Saving slicer widget manifest');
275 fs.write(widgetDataFile, JSON.stringify(data, null, ' '), 'w');
276 }
277
278 console.log('Capture complete');
279 phantom.exit();
280 } catch (e) {
281 console.log("Error capturing page : " + e);
282 phantom.exit(100);
283 }
284 });
285 } else {
286 console.log('Failed to load page');
287 phantom.exit(100);
288 }
289});
Note: See TracBrowser for help on using the repository browser.