Cyranicus commited on
Commit
a301f93
·
1 Parent(s): bfde612

Create sadbooksframe.js

Browse files
Files changed (1) hide show
  1. sadbooksframe.js +382 -0
sadbooksframe.js ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var dom,spaneditor,elements,languageChange=new Event('language'),language="EN",focusedElement,
2
+ translations={
3
+ EN:{
4
+ ELEMENTS_AND_STYLES:"Elements and Styles",
5
+ CONSOLE:"Console",
6
+ DECLARATIONS:"Declarations",
7
+ COMPUTED:"Computed",
8
+ PROPERTIES:"Properties"
9
+ }
10
+ };
11
+ class Tabs {
12
+ constructor(ul,tabs,current) {
13
+ this.ul=ul;
14
+ this.ul.classList.add('tabs');
15
+ this.tabs=tabs;
16
+ this.id=ul.id;
17
+ var t=document.createDocumentFragment();
18
+ for (var tab in tabs) {
19
+ var s=document.createElement("li");
20
+ s.textContent=translations[language][tabs[tab]];
21
+ ((s,w)=>{
22
+ s.addEventListener("language",e=>{
23
+ s.textContent=translations[language][w];
24
+ },false);
25
+ })(s,tabs[tab])
26
+ s.dataset.tab=tab;
27
+ if (tab===current) s.classList.add('active');
28
+ t.appendChild(s);
29
+ }
30
+ this.ul.appendChild(t);
31
+ document.body.classList.add(this.id+'-'+current);
32
+ this.ul.addEventListener("click",e=>{
33
+ if (e.target.tagName==="LI") {
34
+ var c=this.ul.querySelector('.active');
35
+ c.classList.remove('active');
36
+ document.body.classList.remove(this.id+'-'+c.dataset.tab);
37
+ e.target.classList.add('active');
38
+ document.body.classList.add(this.id+'-'+e.target.dataset.tab);
39
+ }
40
+ },false);
41
+ }
42
+ }
43
+ class Element {
44
+ constructor(fakeDOMobj) {
45
+ this.elem=fakeDOMobj;
46
+ this.wrapper=document.createElement("div");
47
+ this.wrapper.classList.add('element-wrapper');
48
+ this.wrapper.classList.add('collapsed');
49
+ this.openBtn=document.createElement("button");
50
+ this.openBtn.addEventListener("click",e=>{
51
+ this.wrapper.classList.contains('collapsed')?this.wrapper.classList.remove('collapsed'):this.wrapper.classList.add('collapsed');
52
+ },false);
53
+ this.openTag=document.createElement("span");
54
+ this.openTag.appendChild(Element.spanClasses({
55
+ '<':'grey',
56
+ [this.elem.tagName]:'red'
57
+ }));
58
+ for (var attr in this.elem.attributes) {
59
+ this.openTag.appendChild(Element.spanClasses({
60
+ ' ':'grey',
61
+ [attr]:'orange',
62
+ '="':'grey',
63
+ [this.elem.attributes[attr]]:'yellow',
64
+ '"':'grey'
65
+ }));
66
+ }
67
+ this.openTag.appendChild(Element.spanClasses({'>':'grey'}));
68
+ this.openTag.classList.add('element-openTag');
69
+ this.children=document.createElement("div");
70
+ this.children.classList.add('element-children');
71
+ this.closeTag=document.createElement("span");
72
+ this.closeTag.appendChild(Element.spanClasses({
73
+ '</':'grey',
74
+ [this.elem.tagName]:'red',
75
+ '>':'grey'
76
+ }));
77
+ this.closeTag.classList.add('element-closeTag');
78
+ this.wrapper.appendChild(this.openBtn);
79
+ this.wrapper.appendChild(this.openTag);
80
+ this.wrapper.appendChild(this.children);
81
+ this.wrapper.appendChild(this.closeTag);
82
+ this.wrapper.addEventListener("click",e=>{
83
+ if (e.target!==this.openBtn&&!this.children.contains(e.target)) {
84
+ if (focusedElement) focusedElement.wrapper.classList.remove('active');
85
+ this.wrapper.classList.add('active');
86
+ focusedElement=this;
87
+ parent.postMessage({
88
+ type:"COMPUTED STYLES",
89
+ set:null,
90
+ path:this.elem.path,
91
+ id:dom.indexOf(this.elem)
92
+ },"*");
93
+ }
94
+ },false);
95
+ if (!fakeDOMobj.children.length) this.wrapper.classList.add('nochildren');
96
+ }
97
+ computedStyles(styles) {
98
+ while (computed.hasChildNodes()) computed.removeChild(computed.lastChild);
99
+ var t=document.createDocumentFragment();
100
+ for (var prop in styles) {
101
+ var row=document.createElement("div"),
102
+ propelem=document.createElement("span"),
103
+ colon=document.createElement("span"),
104
+ scolon=document.createElement("span"),
105
+ value=document.createElement("span");
106
+ propelem.classList.add('syntax');
107
+ propelem.classList.add('cream');
108
+ propelem.textContent=prop;
109
+ colon.classList.add('syntax');
110
+ colon.classList.add('white');
111
+ colon.textContent=": ";
112
+ scolon.classList.add('syntax');
113
+ scolon.classList.add('white');
114
+ scolon.textContent=";";
115
+ value.classList.add('syntax');
116
+ value.classList.add('orange');
117
+ value.textContent=styles[prop];
118
+ ((prop,val,fakedom)=>{
119
+ val.addEventListener("click",e=>{
120
+ editSpan(val,{
121
+ ondone(e) {
122
+ parent.postMessage({
123
+ type:"COMPUTED STYLES",
124
+ set:{
125
+ prop:prop,
126
+ val:e
127
+ },
128
+ path:fakedom.path,
129
+ id:dom.indexOf(fakedom)
130
+ },"*");
131
+ }
132
+ });
133
+ },false);
134
+ })(prop,value,this.elem)
135
+ row.appendChild(propelem);
136
+ row.appendChild(colon);
137
+ row.appendChild(value);
138
+ row.appendChild(scolon);
139
+ t.appendChild(row);
140
+ }
141
+ computed.appendChild(t);
142
+ }
143
+ static spanClasses(obj) {
144
+ var t=document.createDocumentFragment();
145
+ for (var text in obj) {
146
+ var s=document.createElement("span");
147
+ s.textContent=text;
148
+ s.classList.add('syntax');
149
+ s.classList.add(obj[text]);
150
+ if (obj[text]!=='grey')
151
+ (s=>{
152
+ // s.addEventListener("click",e=>{
153
+ // editSpan(s);
154
+ // },false);
155
+ })(s);
156
+ t.appendChild(s);
157
+ }
158
+ return t;
159
+ }
160
+ }
161
+ class PlainText {
162
+ constructor(content) {
163
+ this.content=content;
164
+ this.wrapper=document.createDocumentFragment();
165
+ this.text=document.createElement("span");
166
+ this.text.classList.add('syntax');
167
+ this.text.textContent=content;
168
+ this.wrapper.appendChild(this.text);
169
+ }
170
+ }
171
+ class Text extends PlainText {
172
+ constructor(content) {
173
+ super(content);
174
+ this.text.classList.add('white');
175
+ this.text.classList.add('textNode');
176
+ this.text.innerHTML=Text.showWhiteSpace(this.content);
177
+ }
178
+ static showWhiteSpace(text) {
179
+ var whitespace=false,html='<span class="syntax white">';
180
+ for (var i=0;i<text.length;i++) {
181
+ if (whitespace) {
182
+ if (/\S/.test(text[i])) html+='</span><span class="syntax white">',whitespace=false;
183
+ } else {
184
+ if (/\s/.test(text[i])) html+='</span><span class="syntax grey">',whitespace=true;
185
+ }
186
+ if (whitespace) switch (text[i]) {
187
+ case "\r": html+="¤\n"; break;
188
+ case "\n": html+="¬\n"; break;
189
+ case " ": case "\u00A0": html+="·"; break;
190
+ case "\t": html+="» "; break;
191
+ default:
192
+ html+=text[i];
193
+ }
194
+ else switch (text[i]) {
195
+ case "<": html+="&lt;"; break;
196
+ case ">": html+="&gt;"; break;
197
+ default:
198
+ html+=text[i];
199
+ }
200
+ }
201
+ html+='</span>';
202
+ return html;
203
+ }
204
+ }
205
+ class Comment extends PlainText {
206
+ constructor(content) {
207
+ super(content);
208
+ this.text.classList.add('grey');
209
+ this.text.classList.add('commentNode');
210
+ }
211
+ }
212
+ class Doctype extends PlainText {
213
+ constructor(content) {
214
+ super(content);
215
+ this.text.classList.add('grey');
216
+ this.text.classList.add('doctypeNode');
217
+ }
218
+ }
219
+ function editSpan(span,options={}) {
220
+ var styles=window.getComputedStyle(span);
221
+ span.classList.add('editting');
222
+ spaneditor.style.display='inline-block';
223
+ spaneditor.style.color=styles.color;
224
+ spaneditor.style.font=styles.font;
225
+ spaneditor.value=span.textContent;
226
+ updatePos();
227
+ updateWidth();
228
+ spaneditor.focus();
229
+ function updatePos() {
230
+ var boundingrect=span.getBoundingClientRect();
231
+ spaneditor.style.left=boundingrect.left+'px';
232
+ spaneditor.style.top=boundingrect.top+'px';
233
+ }
234
+ function updateWidth() {
235
+ span.textContent=spaneditor.value;
236
+ spaneditor.style.width=(span.offsetWidth+3)+'px';
237
+ }
238
+ function removeEvents() {
239
+ elements.removeEventListener("scroll",updatePos,false);
240
+ spaneditor.removeEventListener("input",updateWidth,false);
241
+ spaneditor.removeEventListener("blur",removeEvents,false);
242
+ spaneditor.style.display='none';
243
+ span.classList.remove('editting');
244
+ if (options.ondone) options.ondone(spaneditor.value);
245
+ }
246
+ elements.addEventListener("scroll",updatePos,false);
247
+ spaneditor.addEventListener("input",updateWidth,false);
248
+ spaneditor.addEventListener("blur",removeEvents,false);
249
+ }
250
+ function rightClick(items,x,y) {
251
+ var wrapper=document.createElement("ul");
252
+ wrapper.classList.add("context-back");
253
+ function createMenu(parent,array) {
254
+ for (var item of array) {
255
+ if (typeof item==="object") {
256
+ var option=document.createElement("li"),submenu;
257
+ option.classList.add('context-option');
258
+ if (item.disabled) option.classList.add('context-disabled');
259
+ if (item.istoggle) {
260
+ parent.classList.add('context-hastoggles');
261
+ if (item.checked) option.classList.add('context-checked');
262
+ option.addEventListener("click",e=>{
263
+ option.classList.contains('context-checked')?option.classList.remove('context-checked'):option.classList.add('context-checked');
264
+ },false);
265
+ }
266
+ if (item.img) {
267
+ var img=document.createElement("img");
268
+ img.src=item.img;
269
+ img.classList.add('context-image');
270
+ option.appendChild(img);
271
+ }
272
+ if (item.label) option.appendChild(document.createTextNode(translations[language][item.label]));
273
+ if (item.onclick) option.addEventListener("click",e=>{
274
+ if (!submenu.contains(e.target)) item.onclick(e);
275
+ },false);
276
+ if (item.subitems) {
277
+ option.classList.add('context-hassubmenu');
278
+ submenu=document.createElement("ul");
279
+ submenu.classList.add("context-submenu");
280
+ createMenu(submenu,item.subitems);
281
+ option.appendChild(submenu);
282
+ }
283
+ parent.appendChild(option);
284
+ } else {
285
+ var line=document.createElement("li");
286
+ line.classList.add('context-hr');
287
+ parent.appendChild(line);
288
+ }
289
+ }
290
+ }
291
+ createMenu(wrapper,items);
292
+ wrapper.style.left=x+'px';
293
+ wrapper.style.top=y+'px';
294
+ document.body.appendChild(wrapper);
295
+ function stop(e) {
296
+ document.body.removeChild(wrapper);
297
+ wrapper=null;
298
+ document.removeEventListener("click",stop,false);
299
+ document.removeEventListener("contextmenu",stop,false);
300
+ }
301
+ document.addEventListener("click",stop,false);
302
+ document.addEventListener("contextmenu",stop,false);
303
+ }
304
+ window.onload=e=>{
305
+ spaneditor=document.querySelector('#spaneditor'),
306
+ resizer=document.querySelector('#resize'),
307
+ styleresizer=document.querySelector('#resizestyles'),
308
+ styles=document.querySelector('#styles'),
309
+ elements=document.querySelector('#elements'),
310
+ quit=document.querySelector('#quit'),
311
+ computed=document.querySelector('#compstyles'),
312
+ maintabs=new Tabs(document.querySelector('#main'),{
313
+ htmlcss:'ELEMENTS_AND_STYLES',
314
+ console:'CONSOLE'
315
+ },'htmlcss'),
316
+ styletabs=new Tabs(document.querySelector('#styletabs'),{
317
+ declare:'DECLARATIONS',
318
+ compstyles:'COMPUTED',
319
+ props:'PROPERTIES'
320
+ },'compstyles');
321
+ document.addEventListener("contextmenu",e=>{
322
+ rightClick([
323
+ {label:"props"},
324
+ {label:"compstyles",onclick:e=>console.log('hi'),subitems:[
325
+ {label:"declare"}
326
+ ]}
327
+ ],e.clientX,e.clientY);
328
+ e.preventDefault();
329
+ },false);
330
+ resizer.addEventListener("mousedown",e=>{
331
+ parent.postMessage("RESIZE","*");
332
+ e.preventDefault();
333
+ return false;
334
+ },false);
335
+ styleresizer.addEventListener("mousedown",e=>{
336
+ document.body.style.pointerEvents='none';
337
+ function updateWidth(e) {
338
+ styles.style.width=(window.innerWidth-e.clientX)+'px';
339
+ e.preventDefault();
340
+ return false;
341
+ }
342
+ function stop() {
343
+ document.body.style.pointerEvents='all';
344
+ document.removeEventListener("mousemove",updateWidth,false);
345
+ document.removeEventListener("mouseup",stop,false);
346
+ e.preventDefault();
347
+ return false;
348
+ }
349
+ document.addEventListener("mousemove",updateWidth,false);
350
+ document.addEventListener("mouseup",stop,false);
351
+ e.preventDefault();
352
+ return false;
353
+ },false);
354
+ window.addEventListener("message",e=>{
355
+ if (e.data.type) switch (e.data.type) {
356
+ case 'DOM':
357
+ dom=e.data;
358
+ var i=dom.length;
359
+ while (i--) {
360
+ switch (dom[i].type) {
361
+ case 'element':dom[i].representative=new Element(dom[i]);break;
362
+ case 'text':dom[i].representative=new Text(dom[i].text);break;
363
+ case 'comment':dom[i].representative=new Comment(dom[i].text);break;
364
+ case 'doctype':dom[i].representative=new Doctype(dom[i].text);break;
365
+ }
366
+ if (dom[i].children)
367
+ for (var child of dom[i].children)
368
+ if (dom[child].representative)
369
+ dom[i].representative.children.appendChild(dom[child].representative.wrapper);
370
+ }
371
+ elements.appendChild(dom[0].representative.wrapper); // TODO: THIS IS A BAD WAY OF DISPLAYING TOP LEVEL ELEMENTS
372
+ elements.appendChild(dom[1].representative.wrapper);
373
+ break;
374
+ case "RE: COMPUTED STYLES":
375
+ dom[e.data.id].representative.computedStyles(e.data.styles);
376
+ break;
377
+ }
378
+ },false);
379
+ quit.addEventListener("click",e=>{
380
+ parent.postMessage("QUIT","*");
381
+ },false);
382
+ };