VDOM = {}; VDOM.virtual = []; VDOM.stylesCache = {}; VDOM.prepareElements = []; VDOM.disableElements = []; VDOM.cachedSearch = { ids: [], elements: [] } // EXAMPLE GRID VDOM.FORMATS = { PRIORITY: { ROW: 4, COLUMN: 2, ELEMENT: 1 }, STRUCTURE: function(enable) { element = $('#s-'+this.id); if(element.length == 0) return false; structure = this.getStyle(); result = { color: 'yellow', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ROW, conditions: [] } result.conditions.push({ type: 'insertAfter', start: { x: structure.x.start, y: structure.y.start }, end: { x: structure.x.end, y: structure.y.end }, holder: VDOM.fn.holderSettings(structure, 'Вставка новой строки', 'width'), init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); // console.log(structure) VDOM.zones.push(result); }, GRID: function(enable) { grid = this.getStyle(); VDOM.hoverZones.push({ id: this.id, type: 'grid', start: { x: grid.x.start, y: grid.y.start }, end: { x: grid.x.end, y: grid.y.end }, is: { first: this.is('first'), last: this.is('last') }, init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); }, GRIDWRAPPER: function(enable) { if(this.children().length == 0) { grid = this.parent(); grids = grid.getStyle(); result = { color: 'yellow', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ROW, conditions: [] } result.conditions.push({ type: 'append', start: { x: grids.x.start, y: grids.y.start }, end: { x: grids.x.end, y: grids.y.end }, holder: VDOM.fn.holderSettings(grids, 'Вставка новой строки', 'width'), init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); VDOM.zones.push(result); } }, ROW: function(enable) { element = this.getStyle(); parent = this.parent().parent().getStyle(); result = { color: 'red', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ROW, conditions: [] } // FOR TOP if(this.is('first')) { start = { x: parent.x.start, y: parent.y.start }; end = { x: parent.x.end, y: element.y.start+10 }; } else { start = { x: element.x.start, y: element.y.start-10 }; end = { x: element.x.end, y: element.y.start+10 }; } result.conditions.push({ type: 'insertBefore', start: start, end: end, holder: VDOM.fn.holderSettings(element, 'Вставка новой строки', 'width'), elementType: 'column', init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); // FOR BOTTOM if(this.is('last')) { start = { x: parent.x.start, y: element.y.end-10 }; end = { x: parent.x.end, y: parent.y.end }; result.conditions.push({ type: 'insertAfter', start: start, end: end, holder: VDOM.fn.holderSettings(element, 'Вставка новой строки', 'width', true), elementType: 'column', init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); } VDOM.zones.push(result); }, COLUMN: function(enable) { // index = this.index(); // length = this.parent().children().length; // console.log(this); col = this.getStyle(); row = this.parent().getStyle(); grid = this.parent().parent().parent().getStyle(); result = { color: 'blue', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.COLUMN, conditions: [] } // console.log(col, row, grid); if(this.is('last')) { result.conditions.push({ type: 'insertAfter', start: { x: col.x.end-10, y: row.y.start+2 }, end: { x: grid.x.end, y: row.y.end }, holder: VDOM.fn.holderSettings(col, 'Вставка новой колонки', 'height', true), elementType: 'column', init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); } // FOR TOP if(this.is('first')) { start = { x: grid.x.start, y: row.y.start+2 }; end = { x: col.x.start+10, y: row.y.end }; } else { colPrev = this.prev().getStyle(); start = { x: colPrev.x.end-10, y: row.y.start+2 }; end = { x: col.x.start+10, y: row.y.end }; } result.conditions.push({ type: 'insertBefore', start: start, end: end, holder: VDOM.fn.holderSettings(col, 'Вставка новой колонки', 'height'), elementType: 'column', init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); // add zones for mpusemove - grid, element and column settings var colReal = this.getStyle(false); VDOM.hoverZones.push({ id: this.id, type: 'column', start: { x: colReal.x.start, y: colReal.y.start }, end: { x: colReal.x.end, y: colReal.y.end }, is: { first: this.is('first'), last: this.is('last') }, init: function(mouse) { obj = $.extend(true, {}, this); obj.start.x -= 10; obj.end.x += 10; return VDOM.fn.elementSquare.call(obj, mouse); } }); VDOM.zones.push(result); }, SUBROW: function(enable) { index = this.index(); length = this.parent().children().length; subrow = this.getStyle(); row = this.parent().parent().getStyle(); result = { color: 'green', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ELEMENT, conditions: [] } if(index == length-1) { result.conditions.push({ type: 'insertAfter', start: { x: subrow.x.start, y: subrow.y.end }, end: { x: subrow.x.end, y: row.y.end }, holder: VDOM.fn.holderSettings(subrow, 'Вставка нового элемента', 'width', true), init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); } if(index == 0) { result.conditions.push({ type: 'insertBefore', start: { x: subrow.x.start, y: row.y.start }, end: { x: subrow.x.end, y: subrow.y.start }, holder: VDOM.fn.holderSettings(subrow, 'Вставка нового элемента', 'width'), init: function(mouse) { return VDOM.fn.elementSquare.call(this, mouse); } }); } VDOM.zones.push(result); }, ELEMENT: function(enable) { // var t = microtime(true); // console.log('-ELEMENT START-'); element = this.getStyle(); col = this.parent(); cols = col.getStyle(); row = col.parent(); rows = row.getStyle(); // console.log('-ELEMENT '+ ((microtime(true)-t)/1000) +' sec to get styles-'); // t = microtime(true); result = { color: 'green', id: this.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ELEMENT, conditions: [] } result2 = { color: 'green', id: row.id, enable: enable, priority: VDOM.FORMATS.PRIORITY.ELEMENT, conditions: [] } VDOM.hoverZones.push({ id: this.id, type: 'element', is: { first: col.is('first'), last: col.is('last') }, start: { x: element.x.start, y: element.y.start }, end: { x: element.x.end, y: element.y.end }, init: function(mouse) { obj = $.extend(true, {}, this); obj.start.x -= 5; obj.end.x += 5; obj.end.y += 5; return VDOM.fn.elementSquare.call(obj, mouse); } }); // console.log('-ELEMENT '+ ((microtime(true)-t)/1000) +' sec to hover zone-'); // t = microtime(true); // console.log('----') // top result2.conditions.push({ type: 'insertBefore', start: { x: element.x.start, y: element.y.start }, end: { x: element.x.end, y: element.y.end }, holder: VDOM.fn.holderSettings(rows, 'Вставка нового элемента', 'width'), init: function(mouse) { return VDOM.fn.elementDiag.call(this, mouse, 'top'); } }); // bottom result2.conditions.push({ type: 'insertAfter', start: { x: element.x.start, y: element.y.start }, end: { x: element.x.end, y: element.y.end }, holder: VDOM.fn.holderSettings(rows, 'Вставка нового элемента', 'width', true), init: function(mouse) { return VDOM.fn.elementDiag.call(this, mouse, 'bottom'); } }); // console.log('-ELEMENT '+ ((microtime(true)-t)/1000) +' sec to top/bot zone-'); // t = microtime(true); length = this.parent().parent().children().length; VDOM.zones.push(result2); if(length >= 4) return false; // left result.conditions.push({ type: 'column-left', start: { x: element.x.start, y: element.y.start }, end: { x: element.x.end, y: element.y.end }, holder: VDOM.fn.holderSettings(cols, 'Вставка нового элемента', 'height'), init: function(mouse) { return VDOM.fn.elementDiag.call(this, mouse, 'left'); } }); // right result.conditions.push({ type: 'column-right', start: { x: element.x.start, y: element.y.start }, end: { x: element.x.end, y: element.y.end }, holder: VDOM.fn.holderSettings(cols, 'Вставка нового элемента', 'height', true), init: function(mouse) { return VDOM.fn.elementDiag.call(this, mouse, 'right'); } }); VDOM.zones.push(result); // console.log('-ELEMENT '+ ((microtime(true)-t)/1000) +' sec to sub column-'); // t = microtime(true); // console.log('----------'); } }; VDOM.fn = {}; VDOM.fn.holderSettings = function(cords, name, type, isLast) { if(type == 'width') { y = cords.real.y.start; x = cords.real.x.start; } else { y = cords.real.y.start; x = cords.real.x.start; } if(isLast === true) { if(type == 'width') { y = cords.real.y.end; } else { x = cords.real.x.end; } } return { css: { top: y, left: x, }, name: name, type: type, } } VDOM.fn.elementSquare = function(mouse) { return ( (mouse.x > this.start.x && mouse.x < this.end.x) && (mouse.y > this.start.y && mouse.y < this.end.y) ); } VDOM.fn.elementDiag = function(mouse, type) { if(!VDOM.fn.elementSquare.call(this, mouse)) { return false; } width = this.end.x - this.start.x; height = this.end.y - this.start.y; diagLR = height/width * (mouse.x - this.start.x); diagRL = height/width * (this.end.x - mouse.x); position = 'null'; y = mouse.y - this.start.y; if(y < diagLR && y > diagRL) { position = 'right'; } else if(y > diagLR && y < diagRL) { position = 'left'; } else if(y < diagLR && y < diagRL) { position = 'top'; } else if(y > diagLR && y > diagRL) { position = 'bottom'; } return position == type; } VDOM.element = function(params) { // for represent net this.id = 0; this.depth = 0; this.size = 24; this.type = 'null'; this.state = { children: [], parent: null }; for(i in params) { this[i] = params[i]; } } VDOM.element.prototype.getStyle = function(isReal) { if(typeof isReal == 'undefined') isReal = true; prefix = (isReal) ? 't' : 'f'; if(typeof VDOM.stylesCache[this.id+prefix] != 'undefined') { return VDOM.stylesCache[this.id+prefix]; } var element = $('#s-'+this.id); if(element.length == 0) { alert('get style of ' + this.id + ' element is impossible, cause no one'); return false; } position = element.position(); var offset = element.offset(); width = element.outerWidth(); height = element.outerHeight(); offset.right = offset.left + width; offset.bottom = offset.top + height; s = { 'padding-top': 0, 'padding-bottom': 0, 'padding-left': 0, 'padding-right': 0, 'margin-top': 0, 'margin-bottom': 0, 'margin-left': 0, 'margin-right': 0, }; if(typeof Elements[this.id] != 'undefined' && isReal === true) { if(Elements[this.id].type == 'column') { s = Elements[this.id].styles['this> .s-col-wrapper']; } } VDOM.stylesCache[this.id+prefix] = { real: { x: { start: position.left, end: position.left + width, }, y: { start: position.top, end: position.top + height, } }, x: { start: offset.left + s['padding-left'] + s['margin-left'], end: offset.right - s['padding-right'] - s['margin-right'] }, y: { start: offset.top + s['padding-top'] + s['margin-top'], end: offset.bottom - s['padding-bottom'] - s['margin-bottom'] } }; // if(this.id == 1178) console.log(this.id, $('#s-1178').offset().top, offset.top); return VDOM.stylesCache[this.id+prefix]; } VDOM.element.prototype.deepest = function() { this.state.deepest = false; this.deepestRec(this); return this.state.deepest; } VDOM.element.prototype.deepestRec = function(element) { this.state.deepest = element; children = element.children(); if(children.length > 0) { this.deepestRec(children[0]); } } VDOM.element.prototype.toObject = function() { this.elementsSettings = { id: this.id, depth: this.depth, size: this.size, type: this.type, children: [], }; this.toObjectRec(this, this.elementsSettings.children); return this.elementsSettings; } VDOM.element.prototype.toObjectRec = function(e, obj) { var that = this; $.each(e.children(), function(i, element) { e = { id: this.id, depth: this.depth, size: this.size, type: this.type, children: [], }; obj.push(e); that.toObjectRec(element, e.children); }); } VDOM.element.prototype.Elements = function() { this.elementsSettings = {}; this.ElementsRec([this]); return this.elementsSettings; } VDOM.element.prototype.ElementsRec = function(children) { var that = this; $.each(children, function(i, element) { if(typeof Elements[element.id] != 'undefined') that.elementsSettings[element.id] = Elements[element.id]; that.ElementsRec(element.children()); }); } VDOM.element.prototype.parent = function() { // console.log(this.id, this.state.parent, this); return (this.state.parent == null) ? false : this.state.parent; } VDOM.element.prototype.children = function() { return this.state.children; } VDOM.element.prototype.index = function() { node = VDOM.find(this.id, true); index = false; var parent = node.parent || { children: function() { return []; }}; $.each(parent.children(), function(i, element) { if(element.id == node.self.id) { index = i; } }); return index; } VDOM.element.prototype.insertAfter = function(obj) { element = new VDOM.element(obj); element.state.parent = this.state.parent; node = VDOM.find(this.id, true); var index = this.index(); node.parent.children().splice(index+1, 0, element); } VDOM.element.prototype.insertBefore = function(obj) { element = new VDOM.element(obj); element.state.parent = this.state.parent; node = VDOM.find(this.id, true); var index = this.index(); node.parent.children().splice(index, 0, element); } VDOM.element.prototype.append = function(obj) { element = new VDOM.element(obj); element.state.parent = this; // node = VDOM.find(this.id, true); this.state.children.push(element); console.log('-children-', VDOM.find(this.id).children(), element) } VDOM.element.prototype.remove = function() { VDOM.remove(this.id); } VDOM.element.prototype.next = function() { node = VDOM.find(this.id, true); console.warn(node); var index = this.index(); index = (index+1 > node.parent.children().length-1) ? false : index+1; if(index === false) { return false; } else { return node.parent.children()[index]; } } VDOM.element.prototype.last = function() { node = VDOM.find(this.id, true); return node.parent.children()[node.parent.children().length-1]; } VDOM.element.prototype.clone = function() { return $.extend(false, {}, this); } VDOM.element.prototype.prev = function() { node = VDOM.find(this.id, true); index = this.index(); index = (index-1 >= 0) ? index-1 : false; if(index === false) { return false; } else { return node.parent.children()[index]; } } VDOM.element.prototype.siblings = function() { node = VDOM.find(this.id, true); siblings = []; $.each(node.parent.children(), function(i, element) { if(element.id !== node.self.id) { siblings.push(element); } }); return siblings; } VDOM.element.prototype.is = function(type) { if(type == 'last') { index = this.index(); length = node.parent.children().length; return index == length-1; } else if(type == 'first') { index = this.index(); return index === 0; } else { return this.getType() == type; } } VDOM.element.prototype.getType = function() { if(this.depth == 0) { realType = 'structure'; } else if(this.depth % 7 == 0) { realType = 'element'; } else if(this.depth % 6 == 0) { realType = 'sub-column'; } else if(this.depth % 5 == 0) { realType = 'sub-row'; } else if(this.depth % 4 == 0) { realType = 'column'; } else if(this.depth % 3 == 0) { realType = 'row'; } else if(this.depth % 2 == 0) { realType = 'grid-wrapper'; } else { realType = 'grid'; } return realType; } VDOM.element.prototype.info = function(type) { console.log(this); } VDOM.create = function(depth, max, parent) { first = null; last = parent || null; for (var i = depth; i < (max || 8); i++) { element = new VDOM.element({ id: ++Settings.maxID, depth: i }); console.warn('create element', element.id); element.state.parent = last; // MERGE ELEMENT SETTINGS OR CREATE NEW if(i == 7 || i == 4 || i == 1) { VDOM.prepareElements.push(element); } if(depth == i) { first = element; } else { last.state.children.push(element); } last = element; }; return first; } VDOM.start = function() { VDOM.virtual = VDOM.createDOM(); }; VDOM.createDOM = function(element, isPrepare) { if(typeof element == 'undefined') element = $('#editor'); if(typeof isPrepare == 'undefined') isPrepare = false; VDOM.isPrepare = isPrepare; RESULT = []; VDOM.createDOM.rec(element, 1, null, RESULT); return RESULT; }; VDOM.createDOM.rec = function(parent, depth, parentVDOM, nodes) { if(depth % 7 == 0) { elements = parent.children('.s-element'); } else if(depth % 6 == 0) { elements = parent.children('.s-col'); } else if(depth % 5 == 0) { elements = parent.children('.s-col-wrapper').children('.s-row'); } else if(depth % 4 == 0) { elements = parent.children('.s-col'); } else if(depth % 3 == 0) { elements = parent.children('.s-row'); } else if(depth % 2 == 0) { elements = parent.find('.s-grid-wrapper'); } else { elements = parent.children('.s-grid, .s-structure'); } // console.log(elements, depth); elements.each(function() { cDepth = depth; type = $(this).data('type') || null; if(depth == 1) { if($(this).hasClass('s-structure')) { cDepth = 0; } } size = (!$(this).hasClass('s-col')) ? 24 : parseInt($(this).attr('class').match(/size\-(.*)/g)[0].replace(/size-/g, '')); element = new VDOM.element({ id: $(this).data('id'), depth: cDepth, size: size, type: type }); // DO IF IT WILL BE IN USE !!!!!!!!! element.state.parent = parentVDOM; nodes.push(element); if((cDepth == 7 || cDepth == 4 || cDepth == 1) && VDOM.isPrepare) { VDOM.prepareElements.push(element); } VDOM.createDOM.rec($(this), depth+1, element, element.children()); }); }; VDOM.find = function(id, returnParent) { // console.log(id, 'find'); // clean cached search // if(VDOM.cachedSearch.ids.length > 50) { // VDOM.cachedSearch.ids.splice(0, 5); // VDOM.cachedSearch.elements.splice(0, 5); // } // find in cache // cachedSearchIndex = VDOM.cachedSearch.ids.indexOf(id); foundElement = null; // if(cachedSearchIndex > -1 && ['holder'].indexOf(id) == -1 && returnParent != true) { // console.log(id, 'find 1'); // foundElement = VDOM.cachedSearch.elements[cachedSearchIndex]; // return foundElement.self; // } else { // console.log(id, 'find 2'); // } this.isFound = false; this.result = false; this.elements = []; VDOM.findRec(id, { children: function() { return VDOM.virtual; }}, 0); // add to cache // if(this.result.self != null && foundElement == null) { // VDOM.cachedSearch.ids.push(id); // VDOM.cachedSearch.elements.push(this.result); // } if(returnParent === true) { this.result.elements = this.elements.reverse(); return this.result; } else { return this.result.self; } } VDOM.findRec = function(id, parent, index) { var that = this; children = parent.children(); if(children.length > 0) { this.elements[index] = children; } $.each(children, function(i, element) { if(that.isFound) return false; if(element.id == id) { that.result = { self: element, parent: parent }; that.elements = that.elements.slice(0, element.depth) that.isFound = true; return false; } VDOM.findRec(id, element, index+1); }); } /** * ONLY FOR ADDING NEW SECTION * change id of node to merge elements and virtual grid * @param {ovject} nodes virtual grid * @param {pbject} elements element settings * @return {object} updated nodes */ VDOM.generateSection = function(nodes, createNewID, cleanHolders) { ids = []; newNodes = []; VDOM.generateCache = {}; Settings.maxID += 10; if(typeof createNewID == 'undefined') createNewID = true; if(typeof cleanHolders == 'undefined') cleanHolders = false; VDOM.renewIDRec(ids, { id: null, children: [nodes]}, newNodes, createNewID, cleanHolders); return { nodes: newNodes, ids: ids } } VDOM.renewIDRec = function(ids, parent, newNodes, createNewID, cleanHolders) { $.each(parent.children, function(i, element) { if((element.id != 'holder' && cleanHolders) || cleanHolders === false) { if(createNewID) { newID = ++Settings.maxID; } else { newID = element.id; } e = new VDOM.element({ id: newID, depth: element.depth, size: element.size }); VDOM.generateCache[element.id] = e; e.state.parent = VDOM.generateCache[parent.id] || null; if(e.depth == 7 || e.depth == 4 || e.depth == 1) { VDOM.prepareElements.push(e); } newNodes.push(e); ids.push([element.id, newID]); VDOM.renewIDRec(ids, element, e.children(), createNewID, cleanHolders); } }); } VDOM.remove = function(id) { var parent = null, gridWrapper = null, removeElements = [], parentChildren = []; // console.log(id, this.find(id, true).elements); // return false; // console.log('-p-', id, this.find(id, true), '-START-') targetElements = this.find(id, true).elements; targetElements = (targetElements.length == 0) ? [VDOM.virtual] : targetElements; $.each(targetElements, function(i, children) { $.each(children, function (j, v) { if(parent == null) parent = v.parent(); if(v.id == id) { parentChildren = children; removeElements.push(v.id); Holder.scrollRemove(v.id); children.splice(j, 1); return false; } }); if(parent.id && id != 'holder') { /* we have - children - - parent - - - prent of parent REMOVE FROM "PAREPNT OF PARENT" if parent.children().length = 0; */ parentOfParent = parent.parent(); if(children.length == 0 && parentOfParent.depth == 1) { gridWrapper = parent; } if(children.length == 0 && parentOfParent.depth > 1) { if(parent.depth == 1 && parentOfParent === false) { parentChildren = VDOM.virtual; } else { parentChildren = parentOfParent.children(); } console.log(parent, parentOfParent, parentChildren); $.each(parentChildren, function (j, v) { if(v.id == parent.id) { removeElements.push(v.id); Holder.scrollRemove(v.id); // delete parentChildren[j]; // console.log('- 1 -', v.id, parentChildren.slice()) parentChildren.splice(j, 1); // console.log('- 2 -', v.id, parentChildren.slice()); return false; } }); } parent = parentOfParent; } }); if(removeElements.length > 0) { id = removeElements[removeElements.length - 1]; if(id == 'holder') { $('#s-'+id).remove(); // VDOM.zone(); } else { isColumn = false; if(parentChildren.length > 0) { isColumn = (parentChildren[0].depth == 4 || parentChildren[0].depth == 6); } animate = { height: 0, width: 0, opacity: 0 }; if(!isColumn) { delete animate.width; } else { VDOM.column.remove('holder', parentChildren); } if(gridWrapper != null) { empty = HTML.create('grid.empty'); empty.css('opacity', 0).attr({ 'id': 's-empty-'+gridWrapper.id, 'data-id': 's-empty-'+gridWrapper.id }); $('#s-'+gridWrapper.id).html(empty); Animation.add(empty, { opacity: 1 }); } Animation.add($('#s-'+id), animate, function() { $(this).remove(); }); } } if(VDOM.virtual.length == 0) { nothing = $('