/* --- script: Layers.js description: Make widget use layers for all the SVG license: Public domain (http://unlicense.org). authors: Yaroslaff Fedin requires: - LSD.Trait - LSD.Layer - LSD.Module.Styles provides: - LSD.Module.Layers ... */ !function() { LSD.Module.Layers = new Class({ constructors: { layers: function() { this.offset = { inside: {}, outside: {}, padding: {} }; this.shapes = {}; this.style.layers = {}; this.layers = {} } }, addLayer: function(name, value) { var slots = this.style.layers || (this.style.layers = {}); var layer = this.layers[name] = LSD.Layer.get(name, Array.concat(value)); for (var i = 0, painter; painter = layer.painters[i++];) { for (var group = painter.keys, j = 0, property; property = group[j++];) { if (!slots[property]) slots[property] = []; slots[property].push(name); } } }, removeLayer: function(name, value) { var slots = this.style.layers || (this.style.layers = {}); var layer = this.layers[name] = LSD.Layer.get(name, Array.concat(value)); for (var i = 0, painter; painter = layer.painters[i++];) { for (var group = painter.keys, j = 0, property; property = group[j++];) { if (slots[property]) slots[property].erase(name); } } }, renderLayers: function(dirty) { var updated = new FastArray, style = this.style, layers = style.layers, offset = this.offset; for (var property in dirty) if (layers[property]) updated.push.apply(updated, layers[property]); var result = {}; for (var name in this.layers) { if (!updated[name]) continue; var layer = this.layers[name]; var sizes = Object.append({box: this.size}, {size: Object.append({}, this.size)}); result = layer.draw(this, Object.append(result.inside ? {inside: result.inside, outside: result.outside} : {}, sizes)) } var inside = offset.inside = Object.append({left: 0, right: 0, top: 0, bottom: 0}, result.inside); var outside = offset.outside = Object.append({left: 0, right: 0, top: 0, bottom: 0}, result.outside); offset.shape = /*this.shape.getOffset ? this.shape.getOffset(style.current) : */{left: 0, right: 0, top: 0, bottom: 0}; for (var name in this.shapes) { var layer = this.shapes[name]; if (!layer) continue; if (!layer.injected) { for (var layers = Object.keys(this.layers), i = layers.indexOf(layer.name), key, next; key = layers[++i];) { if ((next = this.layers[key]) && next.injected && next.shape) { layer.inject(next.shape, 'before'); break; } } if (!layer.injected) layer.inject(this.getCanvas()); layer.injected = true; } } }, render: function() { var style = this.style, last = style.last, old = style.size, paint = style.paint, changed = style.changed; this.parent.apply(this, arguments); this.setSize(this.getStyles('height', 'width')); var size = this.size; if (size && (!old || (old.width != size.width || old.height != size.height))) { this.fireEvent('resize', [size, old]); changed = paint; } if (Object.getLength(changed) > 0) this.renderLayers(changed); style.changed = {}; style.last = Object.append({}, paint); style.size = Object.append({}, size); this.renderOffsets(); }, renderStyles: function() { this.parent.apply(this, arguments); var style = this.style, current = style.current; Object.append(this.offset, { padding: {left: current.paddingLeft || 0, right: current.paddingRight || 0, top: current.paddingTop || 0, bottom: current.paddingBottom || 0}, margin: {left: current.marginLeft || 0, right: current.marginRight || 0, top: current.marginTop || 0, bottom: current.marginBottom || 0} }); }, renderOffsets: function() { var element = this.element, current = this.style.current, offset = this.offset, // Offset that is provided by: inside = offset.inside, // layers, inside the widget outside = offset.outside, // layers, outside of the widget shape = offset.shape, // shape padding = offset.padding, // padding style declarations margin = offset.margin, // margin style declarations inner = {}, // all inside offsets above, converted to padding outer = {}; // all outside offsets above, converted to margin for (var property in inside) { var cc = property.capitalize(); if (offset.inner) var last = offset.inner[property]; inner[property] = padding[property] + inside[property] + shape[property] + outside[property]; if (last != null ? last != inner[property] : inner[property]) element.style['padding' + cc] = inner[property] + 'px'; if (offset.outer) last = offset.outer[property]; outer[property] = margin[property] - outside[property]; if (last != null ? last != outer[property] : outer[property]) element.style['margin' + cc] = outer[property] + 'px'; } if (inside) Object.append(offset, {inner: inner, outer: outer}); } }); /* Default layer set */ if (!LSD.Layers) LSD.Layers = { shadow: ['size', 'radius', 'shape', 'shadow'], stroke: [ 'radius', 'stroke', 'shape', 'fill'], background: ['size', 'radius', 'stroke', 'offset', 'shape', 'color'], foreground: ['size', 'radius', 'stroke', 'offset', 'shape', 'color'], reflection: ['size', 'radius', 'stroke', 'offset', 'shape', 'color'], icon: ['size', 'scale', 'color', 'stroke', 'offset', 'shape', 'position','shadow'], glyph: ['size', 'scale', 'color', 'stroke', 'offset', 'shape', 'position', 'shadow'] }; /* Pre-generate CSS grammar for layers. It is not required for rendering process itself, because this action is taken automatically when the first widget gets rendered. Declaring layer css styles upfront lets us use it in other parts of the framework (e.g. in stylesheets to validate styles) */ for (var layer in LSD.Layers) LSD.Layer.get(layer, LSD.Layers[layer]); LSD.Options.layers = { add: 'addLayer', remove: 'removeLayer', iterate: true, process: function(value) { return (value === true) ? LSD.Layers : value; } }; }();