Create sadbooksframe.js
Browse files- 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+="<"; break;
|
196 |
+
case ">": html+=">"; 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 |
+
};
|