function CloneObject(what, recursive, asreference) { if($type(what) != 'object') return what; var h = $H(what); h.each(function(v, k){ if($type(v) === 'object' && recursive === true && !asreference.contains(k)){ this[k] = new CloneObject(v, recursive, asreference); }else{ this[k] = v; } }.bind(this)); return this; } //copied from mt1.2 function $empty(){}; String.extend({ stripScripts: function(option){ var scripts = ''; var text = this.replace(/]*>([\s\S]*?)<\/script>/gi, function(){ scripts += arguments[1] + '\n'; return ''; }); if (option === true) $exec(scripts); else if ($type(option) == 'function') option(scripts, text); return text; }, toObject:function() { var o = {}; this.split('&').each(function(pair){ var b = pair.split('='); o[b[0]] = b[1]; }); return o; } }); //copied from mt1.2 function $exec(text){ if (!text) return text; if (window.execScript){ window.execScript(text); } else { var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.text = text; document.head.appendChild(script); document.head.removeChild(script); } return text; }; var FbAsset = Asset.extend({ // only loads in the script if its not already included in the document head javascriptchecked: function(domain, source, properties){ var scripturl = domain + source; var found = document.getElements('script').some(function(s){ return (scripturl == s.src); }); if (found){ return; } properties = $merge({ 'onload': Class.empty }, properties); var script = new Element('script', {'src': source}).addEvents({ 'load': properties.onload, 'readystatechange': function(){ if (this.readyState == 'complete') this.fireEvent('load'); } }); delete properties.onload; return script.setProperties(properties).inject(document.head); } }); Array.extend({ flatten: function(){ var array = []; for (var i = 0, l = this.length; i < l; i++){ var type = $type(this[i]); if (!type) continue; array = array.concat((type == 'array' || type == 'collection' || type == 'arguments') ? Array.flatten(this[i]) : this[i]); } return array; } }); Element.extend({ within: function(p){ var parenttest = this; while(parenttest.parentNode != null){ if(parenttest == p){ return true; } parenttest = parenttest.parentNode; } return false; }, /** hacked from mootools 1.2 clone() allows you to clone and keep ids **/ cloneWithIds: function(contents){ keepid = true; switch ($type(this)){ case 'element': var attributes = {}; for (var j = 0, l = this.attributes.length; j < l; j++){ var attribute = this.attributes[j], key = attribute.nodeName.toLowerCase(); //if (Browser.Engine.trident && (/input/i).test(this.tagName) && (/width|height/).test(key)) continue; var value = (key == 'style' && this.style) ? this.style.cssText : attribute.nodeValue; if (!$chk(value) || key == 'uid' || (key == 'id' && !keepid)) continue; if (value != 'inherit' && ['string', 'number'].contains($type(value))) attributes[key] = value; } var element = new Element(this.nodeName.toLowerCase(), attributes); if (contents !== false){ for (var i = 0, k = this.childNodes.length; i < k; i++){ var child = element.cloneWithIds(this.childNodes[i], true); //if (child) element.grab(child); //cant do grab in mootools 1.1 if (child) { if($type(child) == 'textnode'){ element.appendText(child.nodeValue); }else{ child.injectInside(element); } } } } return element; // case 'textnode': return document.newTextNode(this.nodeValue); //newTextNode doesnt exist in 1.1 case 'textnode': return document.createTextNode(this.nodeValue); } return null; }, down: function(expression, index) { var descendants = this.getChildren(); if (arguments.length == 0) return descendants[0]; return descendants[index]; }, up: function(index) { index = index ? index : 0; var el = this; for(i=0;i<=index;i++){ el = el.getParent(); } return el; }, findUp: function(tag){ if(this.getTag() == tag){ return this; } var el = this; while(el && el.getTag() != tag){ el = el.getParent(); } return el; }, findClassUp: function(classname){ if(this.hasClass(classname)){ return this; } var el = $(this); while(el && !el.hasClass(classname)){ if($type(el.getParent()) != 'element'){ return false; } el = el.getParent(); } return el; }, toggle: function(){ if(this.style.display == 'none'){ this.setStyles({'display':'block'}); }else{ this.setStyles({'display':'none'}); } }, hide: function(){ this.setStyles({'display':'none'}); }, show: function(mode){ this.setStyles({'display':$pick(mode, 'block')}); }, //x, y = mouse location mouseInside: function(x, y){ var coords = this.getCoordinates(); var elLeft = coords.left; var elRight = coords.left + coords.width; var elTop = coords.top; var elBottom = coords.bottom; if( x >= elLeft && x <= elRight ){ if( y >= elTop && y <= elBottom){ return true; } } return false; } }); //Windoo: Mootools window class . Copyright (c) 2007 Yevgen Gorshkov, MIT Style License. /* Script: Fx.Overlay.js Utility class for covering target element or browser window with overlay element. Overlay utility to fix IE6 select tag bug. modified accordingly. Contains , . */ /* Class: Fx.Overlay Overlay class to cover target element content. */ Fx.Overlay = new Class({ options: { 'styles': { 'position': 'absolute', 'top': 0, 'left': 0 } }, /* Property: initialize Creates a new Fx.Overlay object. Arguments: element - element; container element or window object. props - object; the properties to set for overlay element. see Element properties. */ initialize: function(element, props, tag){ this.element = $(element); this.setOptions(props); if ([window, $(document.body)].contains(this.element)){ this.padding = Fx.Overlay.windowPadding; this.container = $(document.body); this.element = window; } else { this.padding = {x: 0, y: 0}; this.container = this.element; } this.overlay = new Element($pick(tag, 'div'), {'styles': {'display': 'none'}}).inject(this.container); this.update(); }, /* Property: show Make overlay element visible. */ show: function(){ this.overlay.setStyle('display', 'block'); return this; }, /* Property: update Recalculate conteiner element scroll size and update overlay element properties. Arguments: props - optional, see Element properties. */ update: function(props){ this.overlay.set($merge(this.options, {'styles': { width: this.element.getScrollWidth() - this.padding.x, height: this.element.getScrollHeight() - this.padding.y }}, props)); return this; }, /* Property: hide Make overlay element invisible. */ hide: function(){ this.overlay.setStyle('display', 'none'); return this; }, /* Property: destroy Destroy overlay element. */ destroy: function(){ this.overlay.remove(true); return this; } }); Fx.Overlay.implement(new Options); Fx.Overlay.windowPadding = (window.ie6) ? {x: 21, y: 4} : {x: 0, y: 0}; Element.$overlay = function(hide, deltaZ){ deltaZ = $pick(deltaZ, 1); if (!this.fixOverlayElement) this.fixOverlayElement = new Element('iframe', { 'properties': {'frameborder': '0', 'scrolling': 'no', 'src': 'javascript:void(0);'}, 'styles': {'position': this.getStyle('position'), 'border': 'none', 'filter': 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)'}}).injectBefore(this); if (hide) return this.fixOverlayElement.setStyle('display', 'none'); var z = this.getStyle('z-index').toInt() || 0; if (z < deltaZ) this.setStyle('z-index', '' + (z = deltaZ + 1) ); var pos = this.getCoordinates(); return this.fixOverlayElement.setStyles({'display' : '', 'z-index': '' + (z - deltaZ), 'left': pos.left + 'px', 'top': pos.top + 'px', 'width': pos.width + 'px', 'height': pos.height + 'px'}); }; /* Class: Element Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>. */ Element.extend({ /* Property: fixOverlay IE only, create or update overlay element to fix 'IE select bug'. From digitarald's extended moo. See Arguments: hide - optional, hide overlay element if true. deltaZ - optional, (overlay z-index) = (element z-index) - deltaZ. defaults to 1. */ fixOverlay: window.ie6 ? Element.$overlay : function(){ return false; }, /* Property: remove Removes the Element from the DOM. Also removes overlay element if present. Arguments: trash - if true empties the element and collects it from garbage. */ remove: function(trash){ if (this.fixOverlayElement){ this.fixOverlayElement.remove(); if (trash){ Garbage.trash([this.fixOverlayElement]); } } if (!this.parentNode) { this.injectInside(document.body); } this.parentNode.removeChild(this); if (trash){ Garbage.trash([this.empty()]); return false; } return this; } }); /* Script: Drag.Multi.js Mootools Drag.Base class extension which adds support for modifying multiple css properties of different elements simultaneously. Contains . License: MIT-style license. Copyright: copyright (c) 2007 Yevgen Gorshkov */ // internal, the default Drag.Transition linear function and it's inverse Drag.Transition = { linear:{ step: function(start, current, direction){ return direction * current - start; }, inverse: function(start, current, direction){ return (start + current) / direction; } } }; /* Class: Drag.Multi Modify multiple css properties of multiple elements based on the position of the mouse. Arguments: options - The options object. Options: handle - required, the $(element) to act as the handle for the draggable elements. onStart - optional, function to execute when the user starts to drag (on mousedown); onBeforeStart - optional, function to execute when the user starts to drag (on mousedown) but before initial properties values are calculated; onComplete - optional, function to execute when the user completes the drag. onSnap - optional, function to execute when the distance between staring point and current mouse position exceeds snap option value onDrag - optional, function to execute at every step of the drag snap - optional, the distance you have to drag before the element starts to respond to the drag. defaults to false */ Drag.Multi = Drag.Base.extend({ options: { handle: false, onStart: Class.empty, onBeforeStart: Class.empty, onComplete: Class.empty, onDrag: Class.empty, snap: 6 }, elementOptions: { unit: 'px', direction: 1, limit: false, grid: false, bind: false, fn: Drag.Transition.linear }, initialize: function(options){ this.setOptions(options); this.handle = $(this.options.handle); this.element = []; this.mouse = {'start': {}, 'now': {}}; this.modifiers = {}; this.bound = { 'start': this.start.bindWithEvent(this), 'check': this.check.bindWithEvent(this), 'drag': this.drag.bindWithEvent(this), 'stop': this.stop.bind(this) }; this.attach(); if (this.options.initialize) this.options.initialize.call(this); }, add: function(el, options, bind){ el = $(el); if (!$defined(bind)) bind = {}; var result = {}; for (var z in options){ if ($type(options[z]) != 'object' || !$defined(options[z].style)) continue; if (!$defined(this.modifiers[z])) this.modifiers[z] = []; var mod = $merge(this.elementOptions, options[z], {modifier: z, element: el, bind: false, binded: false}); if (bind[z]){ mod.bind = bind[z]; mod.bind.binded = true; } var sign = mod.style.slice(0, 1); if (sign == '-' || sign == '+'){ mod.direction = (sign + 1).toInt(); mod.style = mod.style.slice(1); } this.modifiers[z].push(mod); result[z] = mod; } if (!this.element.contains(el)) this.element.push(el); return result; }, remove: function(el){ el = $(el); for (var z in this.modifiers) this.modifiers[z] = this.modifiers[z].filter(function(e){ return el != e.element; }); this.element.remove(el); return this; }, detach: function(mod){ for (var z in mod) if ($type(mod[z]) == 'object' && !mod[z].binded) this.modifiers[z].remove(mod[z]); return this; }, start: function(event){ this.fireEvent('onBeforeStart', this.element); this.mouse.start = event.page; for (var z in this.modifiers){ var mouse = this.mouse.start[z]; this.modifiers[z].each(function(mod){ mod.now = mod.element.getStyle(mod.style).toInt(); mod.start = mod.fn.step(mod.now, mouse, mod.direction, true); mod.$limit = []; var limit = mod.limit; if (limit) for (var i = 0; i < 2; i++){ if ($chk(limit[i])) mod.$limit[i] = ($type(limit[i]) == 'function') ? limit[i](mod) : limit[i]; } }, this); } document.addListener('mousemove', this.bound.check); document.addListener('mouseup', this.bound.stop); this.fireEvent('onStart', this.element); event.stop(); }, modifierUpdate: function(mod){ var z = mod.modifier, mouse = this.mouse.now[z]; mod.out = false; mod.now = mod.fn.step(mod.start, mod.bind ? mod.bind.inverse : mouse, mod.direction); if (mod.$limit && $chk(mod.$limit[1]) && (mod.now > mod.$limit[1])){ mod.now = mod.$limit[1]; mod.out = true; } else if (mod.$limit && $chk(mod.$limit[0]) && (mod.now < mod.$limit[0])){ mod.now = mod.$limit[0]; mod.out = true; } if (mod.grid) mod.now -= ((mod.now + mod.grid/2) % mod.grid) - mod.grid/2; if (mod.binded) mod.inverse = mod.fn.inverse(mod.start, mod.now, mod.direction); mod.element.setStyle(mod.style, mod.now + mod.unit); }, drag: function(event){ this.mouse.now = event.page; for (var z in this.modifiers) this.modifiers[z].each(this.modifierUpdate, this); this.fireEvent('onDrag', this.element); event.stop(); } }); /* Script: Drag.Resize.js Mootools Drag extension class for creating elements resizable in 8 directions. Contains , . */ Drag.Multi.$direction = { east: { 'x':1 }, west: { 'x':-1 }, north: { 'y':-1 }, south: { 'y':1 }, nw: { 'x':-1, 'y':-1 }, ne: { 'x':1, 'y':-1 }, sw: { 'x':-1, 'y':1 }, se: { 'x':1, 'y':1 } }; /* Class: Drag.Resize Extends , has additional functionality for resizing an element into 8 direction. Arguments: el - the $(element) to apply the resize to. options - the options object. Options: zIndex - optional, resize shade z-index; moveLimit - object, limit for element moving (resize in negative directions), see Limit below; resizeLimit - object, limit for element resizing, see Limit below; grid - optional, distance in px for snap-to-grid dragging; modifiers - an object. see Modifiers below; container - an element, will fill automatically limiting options based on the $(element) size and position. if false no limiting is applied. defaults to null (parentNode); preserveRatio - boolean, preserve initial element aspect ratio during resize. defaults to false; ghost - optional, show wired ghpot element during resize and update the element size and position after resize is completed; snap - optional, the distance you have to drag before the element starts to respond to the drag. defaults to 6; direction - object, see Direction below; limiter - object, see Limiter below; moveLimiter - object, see Limiter below; ghostClass - optional, wired ghost element class name; classPrefix - optional, class name prefix to add to sizer elements; hoverClass - optional, class name added to element onmouserover; shadeBackground - optional, background CSS property value for resize shade element (contains path to 1x1 px transparent gif image file); Direction: east - east direction: { 'x':1 }, west - west direction: { 'x':-1 }, north - north direction: { 'y':-1 }, south - south direction: { 'y':1 }, nw - north-west direction: { 'x':-1, 'y':-1 }, ne - north-east direction: { 'x':1, 'y':-1 }, sw - south-west direction: { 'x':-1, 'y':1 }, se - south-east direction: { 'x':1, 'y':1 } Limiter: x - internal; {'-1': ['left', 'right'], '1': ['right', 'left']}, y - internal; {'-1': ['top', 'bottom'], '1': ['bottom', 'top']} Events: onBuild - optional, function to execute when resize handle is built; onBeforeStart - optional, function to execute when the user starts resizing but before initial properties values are calculated; onStart - optional, function to execute when the user starts resizing; onResize - optional, function to execute at every resize step; onComplete - optional, function to execute when the user completes the resize; */ Drag.Resize = new Class({ options:{ zIndex: 10000, moveLimit: false, resizeLimit: {'x': [0], 'y': [0]}, grid: false, modifiers: {'x': 'left', 'y': 'top', 'width': 'width', 'height': 'height'}, container: null, // false == no caintainer, null == container is parentNode preserveRatio: false, ghost: false, snap: 6, direction: Drag.Multi.$direction, limiter:{ 'x': {'-1': ['left', 'right'], '1': ['right', 'left']}, 'y': {'-1': ['top', 'bottom'], '1': ['bottom', 'top']} }, moveLimiter:{ 'x': ['left', 'right'], 'y': ['top', 'bottom'] }, ghostClass: 'ghost-sizer sizer-visible', classPrefix: 'sizer sizer-', hoverClass: 'sizer-visible', shadeBackground: 'transparent url(s.gif)', onBuild: Class.empty, onBeforeStart: Class.empty, onStart: Class.empty, onSnap: Class.empty, onResize: Class.empty, onComplete: Class.empty }, initialize: function(el, options){ var self = this; this.element = this.el = $(el); this.fx = {}; this.binds = {}; this.bound = {}; this.setOptions(options); this.options.container = this.options.container === null ? this.el.getParent() : $(this.options.container); if ($type(this.options.direction) == 'string'){ if (dir == 'all'){ this.options.direction = Drag.Multi.$direction; } else { var dir = this.options.direction.split(/\s+/); this.options.direction = {}; dir.each(function(d){ this[d] = Drag.Multi.$direction[d]; }, this.options.direction); } } var ce = this.el.getCoordinates(), positionStyle = this.el.getStyle('position'); this.el.setStyles({'width': ce.width, 'height': ce.height}); if (this.options.container){ if (!(['relative', 'fixed'].contains(positionStyle))){ var cc = this.options.container.getCoordinates(); this.el.setStyles({'left': ce.left - cc.left, 'top': ce.top - cc.top}); } this.options.moveLimit = $merge({'x': [0], 'y': [0]}, this.options.moveLimit); } if (this.options.preserveRatio){ var R = ce.width / ce.height; // fix limits according to aspect ratio // FIXME how to process dynamic limits? // border limits do not work well too... var rlim = self.options.resizeLimit; var fix = function(z1, z2, op, no, coeff){ if(rlim && rlim[z1] && rlim[z2] && rlim[z1][no] && rlim[z2][no]) rlim[z1][no] = Math[op]( rlim[z1][no], coeff * rlim[z2][no] ); }; fix('x','y','max',0,R); fix('y','x','max',0,1/R); fix('x','y','min',1,R); fix('y','x','min',1,1/R); this.aspectStep = { x: {step: function(s, c, d){ return d * c / R - s; }}, y: {step: function(s, c, d){ return d * c * R - s; }} }; this.options.direction = $merge(this.options.direction); ['nw','ne','sw','se'].each(function(z){ delete this[z]; }, this.options.direction); } if (this.options.ghost){ this.ghost = new Element('div', {'class': this.options.ghostClass, 'styles': {'display': 'none'}}).injectAfter(this.el); for (var d in this.options.direction) this.ghost.adopt(new Element('div', {'class': this.options.classPrefix + d})); } var rOpts = { snap: this.options.snap, onBeforeStart: function(){ self.fireEvent('onBeforeStart', this); self.started = true; this.shade = new Fx.Overlay(window, {'styles': { 'position': positionStyle, 'cursor': this.options.handle.getStyle('cursor'), 'background': self.options.shadeBackground, 'z-index': self.options.zIndex + 1 }}).show(); if (self.ghost){ var ce = self.el.getCoordinates(); self.ghost.setStyles({ 'display': 'block', 'z-index': self.options.zIndex, 'left': self.el.getStyle('left'), 'top': self.el.getStyle('top'), 'width': ce.width, 'height': ce.height }); for (var z in this.modifiers) this.modifiers[z].each(function(mod){ if (mod.element === self.ghost) mod.element.setStyle(mod.style, self.el.getStyle(mod.style)); }); if (self.options.hoverClass) self.el.removeClass(self.options.hoverClass); } }, onSnap: function(){ self.fireEvent('onSnap', this); }, onStart: function(){ self.fireEvent('onStart', this); }, onDrag: function(){ self.fireEvent('onResize', this); }, onComplete: function(){ self.started = false; if (self.options.hoverClass) self.el.removeClass(self.options.hoverClass); this.shade.destroy(); if (self.ghost){ for (var z in this.modifiers){ this.modifiers[z].each(function(mod){ if (mod.element === self.ghost) self.el.setStyle(mod.style, mod.now + mod.unit); }); } self.ghost.setStyle('display', 'none'); } self.fireEvent('onComplete', this); } }; var rlimitFcn = function(sign, props, limit){ if (!self.options.container) return limit; if (!limit) limit = [0]; var generator = function(lim){ return function(mod){ var cc = self.options.container.getCoordinates(), ec = mod.element.getCoordinates(); var value = sign * (cc[props[0]] - ec[props[1]]); switch ($type(lim)){ case 'number': return Math.min(value, lim); case 'function': return Math.min(value, lim(mod)); default: return value; } }; }; return [limit[0], generator(limit[1])]; }; var mlimitFcn = function(props, limit, rlimit){ var container = self.options.container; var generator = function(lim, rlim, op, rdef){ if (!$type(rlim)) rlim = rdef; var lim_type = $type(lim); if (rlim === null) return lim_type == 'function' ? lim : function(){ return lim; }; return function(mod){ var cc = container.getCoordinates(), ec = mod.element.getCoordinates(); var value = ec[props[1]] - cc[props[0]] - rlim; switch (lim_type){ case 'number': return Math[op](value, lim); case 'function': return Math[op](value, lim(mod)); default: return value; } }; }; if (!container){ if (!limit) limit=false; container = self.el.getParent(); } else if (!limit) limit=[0]; return [generator(limit[0],rlimit[1],'max',null), generator(limit[1],rlimit[0],'min',limit[1])]; }; var opt = this.options, el = this.ghost ? this.ghost : this.el; if ($type(opt.grid) == 'number') opt.grid = {'x': opt.grid, 'y': opt.grid}; for (var d in opt.direction){ var mod = opt.direction[d]; rOpts.handle = new Element('div', {'class': opt.classPrefix + d}); var drag = this.fx[d] = new Drag.Multi(rOpts); var resizeLimit = { 'x': rlimitFcn(mod.x, opt.limiter.x['' + mod.x], opt.resizeLimit.x), 'y': rlimitFcn(mod.y, opt.limiter.y['' + mod.y], opt.resizeLimit.y) }; var moveOpts = {}; for (var z in mod){ if (mod[z] < 0){ moveOpts[z] = { limit: mlimitFcn(opt.moveLimiter[z], opt.moveLimit[z], opt.resizeLimit[z]), style: opt.modifiers[z], grid: opt.grid.x }; } } var binds = {move: drag.add(el, moveOpts)}, resize = {opts: {}, bind: {}}; this.binds[d] = binds; if ($defined(mod.x)){ resize.opts.x = { limit: mod.x < 0 ? false : resizeLimit.x, grid: mod.x < 0 ? false : opt.grid.x, style: opt.modifiers.width, direction: mod.x }; if (mod.x < 0) resize.bind.x = binds.move.x; } if ($defined(mod.y)){ resize.opts.y = { limit: mod.y < 0 ? false : resizeLimit.y, grid: mod.y < 0 ? false : opt.grid.y, style: opt.modifiers.height, direction: mod.y }; if (mod.y < 0) resize.bind.y = binds.move.y; } binds.resize = drag.add(el, resize.opts, resize.bind); if (opt.preserveRatio){ var aspect = { 'x': { fn: this.aspectStep.x, style: ($defined(mod.x)) ? opt.modifiers.height : null, direction: mod.x }, 'y': { fn: this.aspectStep.y, style: ($defined(mod.y)) ? opt.modifiers.width : null, direction: mod.y } }; binds.aspect = drag.add(el, aspect, binds.resize); } this.fireEvent('onBuild', [d, binds]); } this.bound = (!this.options.hoverClass) ? {} : { 'mouseenter': function(ev){ this.addClass(self.options.hoverClass); }, 'mouseleave': function(ev){ if(!self.started) this.removeClass(self.options.hoverClass); } }; this.attach(); if (this.options.initialize) this.options.initialize(); }, /* Property: add Call given function for each instance created by . Emulates onBuild event execution. Arguments: callback - the callback function called with arguments [direction, bind] */ add: function(callback){ for (var d in this.options.direction) callback.call(this, d, this.binds[d]); }, /* Property: attach Attach the effect to the element. */ attach: function(){ $each(this.bound, function(fn, ev){ this.addEvent(ev, fn) }, this.el); for (var z in this.fx) this.element.adopt(this.fx[z].handle); return this; }, /* Property: detach Detach the effect from the element. */ detach: function(){ $each(this.bound, function(fn, ev){ this.removeEvent(ev, fn) }, this.el); for (var z in this.fx) this.fx[z].handle.remove(); return this; }, /* Property: stop Stop the effect and collect the garbage. */ stop: function(){ this.detach(); var garbage = [this.ghost]; for (var z in this.fx) garbage.push(this.fx[z].handle); Garbage.trash(garbage); this.fx = this.bound = this.binds = {}; } }); Drag.Resize.implement(new Events, new Options); /* Class: Element Custom class to allow all of its methods to be used with any DOM element via the dollar function <$>. */ Element.extend({ /* Property: makeResizable Makes an element resizable (by dragging) with the supplied options. Arguments: options - see and for acceptable options. Falls back to if handle options set. */ makeResizable: function(options){ options = options || {}; if (options.handle) return new Drag.Base(this, $merge({modifiers: {'x': 'width', 'y': 'height'}}, options)); return new Drag.Resize(this, options); } }); /** * Misc. functions, nothing to do with Mootools ... we just needed * some common js include to put them in! */ function fconsole(thing) { if (typeof(window["console"]) != "undefined") { console.log(thing); } } /* Slimbox v1.41 - The ultimate lightweight Lightbox clone by Christophe Beyls (http://www.digitalia.be) - MIT-style license. Inspired by the original Lightbox v2 by Lokesh Dhakar. */ var Lightbox = { init: function(options){ this.options = $extend({ resizeDuration: 400, resizeTransition: false, // default transition initialWidth: 250, initialHeight: 250, animateCaption: true, showCounter: true }, options || {}); this.anchors = []; $each(document.links, function(el){ if (el.rel && el.rel.test(/^lightbox/i)){ el.onclick = this.click.pass(el, this); this.anchors.push(el); } }, this); this.eventKeyDown = this.keyboardListener.bindAsEventListener(this); this.eventPosition = this.position.bind(this); this.overlay = new Element('div', {'id': 'lbOverlay'}).injectInside(document.body); this.center = new Element('div', {'id': 'lbCenter', 'styles': {'width': this.options.initialWidth, 'height': this.options.initialHeight, 'marginLeft': -(this.options.initialWidth/2), 'display': 'none'}}).injectInside(document.body); this.image = new Element('div', {'id': 'lbImage'}).injectInside(this.center); this.prevLink = new Element('a', {'id': 'lbPrevLink', 'href': '#', 'styles': {'display': 'none'}}).injectInside(this.image); this.nextLink = this.prevLink.clone().setProperty('id', 'lbNextLink').injectInside(this.image); this.prevLink.onclick = this.previous.bind(this); this.nextLink.onclick = this.next.bind(this); this.bottomContainer = new Element('div', {'id': 'lbBottomContainer', 'styles': {'display': 'none'}}).injectInside(document.body); this.bottom = new Element('div', {'id': 'lbBottom'}).injectInside(this.bottomContainer); new Element('a', {'id': 'lbCloseLink', 'href': '#'}).injectInside(this.bottom).onclick = this.overlay.onclick = this.close.bind(this); this.caption = new Element('div', {'id': 'lbCaption'}).injectInside(this.bottom); this.number = new Element('div', {'id': 'lbNumber'}).injectInside(this.bottom); new Element('div', {'styles': {'clear': 'both'}}).injectInside(this.bottom); var nextEffect = this.nextEffect.bind(this); this.fx = { overlay: this.overlay.effect('opacity', {duration: 500}).hide(), resize: this.center.effects($extend({duration: this.options.resizeDuration, onComplete: nextEffect}, this.options.resizeTransition ? {transition: this.options.resizeTransition} : {})), image: this.image.effect('opacity', {duration: 500, onComplete: nextEffect}), bottom: this.bottom.effect('margin-top', {duration: 400, onComplete: nextEffect}) }; this.preloadPrev = new Image(); this.preloadNext = new Image(); }, click: function(link){ if (link.rel.length == 8) return this.show(link.href, link.title); var j, imageNum, images = []; this.anchors.each(function(el){ if (el.rel == link.rel){ for (j = 0; j < images.length; j++) if(images[j][0] == el.href) break; if (j == images.length){ images.push([el.href, el.title]); if (el.href == link.href) imageNum = j; } } }, this); return this.open(images, imageNum); }, show: function(url, title){ return this.open([[url, title]], 0); }, open: function(images, imageNum){ this.images = images; this.position(); this.setup(true); this.top = window.getScrollTop() + (window.getHeight() / 15); this.center.setStyles({top: this.top, display: ''}); this.fx.overlay.start(0.8); return this.changeImage(imageNum); }, position: function(){ this.overlay.setStyles({'top': window.getScrollTop(), 'height': window.getHeight()}); }, setup: function(open){ var elements = $A(document.getElementsByTagName('object')); elements.extend(document.getElementsByTagName(window.ie ? 'select' : 'embed')); elements.each(function(el){ if (open) el.lbBackupStyle = el.style.visibility; el.style.visibility = open ? 'hidden' : el.lbBackupStyle; }); var fn = open ? 'addEvent' : 'removeEvent'; window[fn]('scroll', this.eventPosition)[fn]('resize', this.eventPosition); document[fn]('keydown', this.eventKeyDown); this.step = 0; }, keyboardListener: function(event){ switch (event.keyCode){ case 27: case 88: case 67: this.close(); break; case 37: case 80: this.previous(); break; case 39: case 78: this.next(); } }, previous: function(){ return this.changeImage(this.activeImage-1); }, next: function(){ return this.changeImage(this.activeImage+1); }, changeImage: function(imageNum){ if (this.step || (imageNum < 0) || (imageNum >= this.images.length)) return false; this.step = 1; this.activeImage = imageNum; this.bottomContainer.style.display = this.prevLink.style.display = this.nextLink.style.display = 'none'; this.fx.image.hide(); this.center.className = 'lbLoading'; this.preload = new Image(); this.preload.onload = this.nextEffect.bind(this); this.preload.src = this.images[imageNum][0]; return false; }, nextEffect: function(){ switch (this.step++){ case 1: this.center.className = ''; this.image.style.backgroundImage = 'url('+this.images[this.activeImage][0]+')'; this.image.style.width = this.bottom.style.width = this.preload.width+'px'; this.image.style.height = this.prevLink.style.height = this.nextLink.style.height = this.preload.height+'px'; this.caption.setHTML(this.images[this.activeImage][1] || ''); this.number.setHTML((!this.options.showCounter || (this.images.length == 1)) ? '' : 'Image '+(this.activeImage+1)+' of '+this.images.length); if (this.activeImage) this.preloadPrev.src = this.images[this.activeImage-1][0]; if (this.activeImage != (this.images.length - 1)) this.preloadNext.src = this.images[this.activeImage+1][0]; if (this.center.clientHeight != this.image.offsetHeight){ this.fx.resize.start({height: this.image.offsetHeight}); break; } this.step++; case 2: if (this.center.clientWidth != this.image.offsetWidth){ this.fx.resize.start({width: this.image.offsetWidth, marginLeft: -this.image.offsetWidth/2}); break; } this.step++; case 3: this.bottomContainer.setStyles({top: this.top + this.center.clientHeight, height: 0, marginLeft: this.center.style.marginLeft, display: ''}); this.fx.image.start(1); break; case 4: if (this.options.animateCaption){ this.fx.bottom.set(-this.bottom.offsetHeight); this.bottomContainer.style.height = ''; this.fx.bottom.start(0); break; } this.bottomContainer.style.height = ''; case 5: if (this.activeImage) this.prevLink.style.display = ''; if (this.activeImage != (this.images.length - 1)) this.nextLink.style.display = ''; this.step = 0; } }, close: function(){ if (this.step < 0) return; this.step = -1; if (this.preload){ this.preload.onload = Class.empty; this.preload = null; } for (var f in this.fx) this.fx[f].stop(); this.center.style.display = this.bottomContainer.style.display = 'none'; this.fx.overlay.chain(this.setup.pass(false, this)).start(0); return false; } }; window.addEvent('domready', Lightbox.init.bind(Lightbox)); /** * @author Robert */ var fabrikForm = new Class( { initialize : function(id) { this.id = id; this.options = Object.extend( { 'admin':false, 'postMethod':'post', 'primaryKey':null, 'error':'', 'delayedEvents':false, 'updatedMsg':'Form saved', 'liveSite':'', 'pages':[], 'page_save_groups':[], 'start_page':0, 'ajaxValidation':false, 'customJsAction':'', 'plugins':[], 'ajaxmethod':'post', 'mooversion':1.1 }, arguments[1] || {}); this.options.pages = $H(this.options.pages); this.subGroups = $H({}); this.lang = Object.extend( { 'validation_altered_content' :'The validation has altered your content:', 'validating' :'Validating', 'success' :'Success', 'nodata': 'No data', validation_error:'Validation error', form_saved:'Form saved' }, arguments[2] || {}); this.currentPage = this.options.start_page; this.formElements = $H({}); this.delGroupJS = $H({}); this.duplicateGroupJS = $H({}); this.listenTo = $A([]); this.bufferedEvents = $A([]); this.duplicatedGroups = $H(); this.clickDeleteGroup = this.deleteGroup.bindAsEventListener(this); this.clickDuplicateGroup = this.duplicateGroup.bindAsEventListener(this); this.fx = {}; this.fx.elements = []; this.fx.validations = {}; window.addEvent('domready', this.setUpAll.bindAsEventListener(this)); }, setUpAll: function() { this.setUp(); this.winScroller = new Fx.Scroll(window); this.watchAddOptions(); $H(this.options.hiddenGroup).each(function(v, k){ if(v == true){ var subGroup = $('group'+k).getElement('.fabrikSubGroup'); this.subGroups.set(k, subGroup.cloneWithIds()); this.hideLastGroup(k, subGroup); } }.bind(this)); // get an int from which to start incrementing for each repeated group id // dont ever decrease this value when deleteing a group as it will cause all sorts of // reference chaos with cascading dropdowns etc this.repeatGroupMarkers = $H({}); this.form.getElements('.fabrikGroup').each(function(group){ var id = group.id.replace('group', ''); var c = group.getElements('.fabrikSubGroup').length; this.repeatGroupMarkers.set(id, c); }.bind(this)); }, watchAddOptions : function() { this.fx.addOptions = []; this.getForm().getElements('.addoption').each( function(d) { var a = d.getParent().getElement('.toggle-addoption'); var mySlider = new Fx.Slide(d, { duration :500 }); mySlider.hide(); a.addEvent('click', function(e) { new Event(e).stop(); mySlider.toggle(); }); }); }, setUp : function() { this.form = this.getForm(); this.watchGroupButtons(); if (this.options.editable) { this.watchSubmit(); } this.createPages(); this.watchClearSession(); }, getForm : function() { this.form = $(this.getBlock()); return this.form; }, getBlock: function(){ return this.options.editable == true ? 'form_' + this.id : 'details_' + this.id; }, // id is the element or group to apply the fx TO, triggered from another // element addElementFX : function(id) { id = id.replace('fabrik_trigger_', ''); if (id.slice(0, 6) == 'group_') { id = id.slice(6, id.length); var k = id; var c = $(id); } else { id = id.slice(8, id.length); k = 'element' + id; if (!$(id)) { return; } c = $(id).findClassUp('fabrikElementContainer'); } if (c) { // c will be the
  • element - you can't apply fx's to this as it makes the // DOM squiffy with // multi column rows, so get the li's content and put it inside a div which // is injected into c // apply fx to div rather than li - damn im good if ((c).getTag() == 'li') { var fxdiv = new Element('div').adopt(c.getChildren()); c.empty(); fxdiv.injectInside(c); } else { fxdiv = c; } var opts = { duration :800, transition :Fx.Transitions.Sine.easeInOut }; this.fx.elements[k] = {}; this.fx.elements[k].css = fxdiv.effect('opacity', opts); if ($type(fxdiv) != false) { this.fx.elements[k]['slide'] = new Fx.Slide(fxdiv, opts); } else { this.fx.elements[k]['slide'] = null; } } }, doElementFX : function(id, method) { id = id.replace('fabrik_trigger_', ''); if (id.slice(0, 6) == 'group_') { id = id.slice(6, id.length); //wierd fix? if (id.slice(0, 6) == 'group_') id = id.slice(6, id.length); var k = id; var groupfx = true; } else { groupfx = false; id = id.slice(8, id.length); k = 'element' + id; } var fx = this.fx.elements[k]; if (!fx) { return; } var fxElement = groupfx ? fx.css.element : fx.css.element.findClassUp('fabrikElementContainer'); switch (method) { case 'show': fxElement.removeClass('fabrikHide'); fx.css.set(1); fx.css.element.show(); break; case 'hide': fxElement.addClass('fabrikHide'); fx.css.set(0); fx.css.element.hide(); break; case 'fadein': fxElement.removeClass('fabrikHide'); if (fx.css.lastMethod !== 'fadein') { fx.css.element.show(); fx.css.start(0, 1); } break; case 'fadeout': if (fx.css.lastMethod !== 'fadeout') { fx.css.start(1, 0).chain( function() { fx.css.element.hide(); fxElement.addClass('fabrikHide'); }); } break; case 'slide in': fx.slide.slideIn(); break; case 'slide out': fx.slide.slideOut(); fxElement.removeClass('fabrikHide'); break; case 'slide toggle': fx.slide.toggle(); //fxElement.toggleClass('fabrikHide'); break; } fx.lastMethod = method; this.runPlugins('onDoElementFX', null); }, watchClearSession : function() { if (this.form && this.form.getElement('.clearSession')) { this.form.getElement('.clearSession').addEvent('click', function(e) { new Event(e).stop(); this.form.getElement('input[name=task]').value = 'removeSession'; this.clearForm(); this.form.submit(); }.bind(this)); } }, createPages : function() { if (this.options.pages.keys().length > 1) { //wrap each page in its own div this.options.pages.each(function(page, i){ var p = new Element('div', {'class': 'page', 'id':'page_' + i}); p.injectBefore($('group'+page[0])); page.each(function(group){ p.adopt($('group'+group)); }); }); if ($('fabrikSubmit' + this.id)) { $('fabrikSubmit' + this.id).disabled = "disabled"; } this.form.getElement('.fabrikPagePrevious').disabled = "disabled"; this.form.getElement('.fabrikPageNext').addEvent('click', this._doPageNav.bindAsEventListener(this, [ 1 ])); this.form.getElement('.fabrikPagePrevious').addEvent('click', this._doPageNav.bindAsEventListener(this, [ -1 ])); this.setPageButtons(); this.hideOtherPages(); } }, _doPageNav : function(e, dir) { this.form.getElement('.fabrikMainError').addClass('fabrikHide'); //if tip shown at bottom of long page and next page shorter we need to move the tip to //the top of the page to avoid large space appearing at the bottom of the page. if($type(document.getElement('.tool-tip')) !== false){ document.getElement('.tool-tip').setStyle('top',0); } var url = this.options.liveSite + 'index.php?option=com_fabrik&controller=form&format=raw&task=ajax_validate&form_id=' + this.id; oPackage.startLoading(this.getBlock(), 'validating'); // only validate the current groups elements, otherwise validations on //other pages cause the form to show an error. var groupId = this.options.pages.get(this.currentPage.toInt()); var d = $H(this.getFormData()); d.set('controller', 'form'); d.set('task', 'ajax_validate'); d.set('fabrik_postMethod', 'ajax'); d.set('format', 'raw'); d = this._prepareRepeatsForAjax(d); var myAjax = new Ajax(url, { method :this.options.ajaxmethod, data :d, onComplete : function(r) { oPackage.stopLoading(this.getBlock()); r = Json.evaluate(r); if (this._showGroupError(r, d) == false) { this.changePage(dir); this.saveGroupsToDb(); } }.bind(this) }).request(); var event = new Event(e).stop(); }, saveGroupsToDb : function() { if(!this.runPlugins('saveGroupsToDb', null)){ return; } var orig = this.form.getElement('input[name=format]').value; var origprocess = this.form.getElement('input[name=task]').value; this.form.getElement('input[name=format]').value = 'raw'; this.form.getElement('input[name=task]').value = 'savepage'; var url = this.options.liveSite + 'index.php?option=com_fabrik&format=raw&page=' + this.currentPage; oPackage.startLoading(this.getBlock(), 'saving page'); var data = this.getFormData(); new Ajax(url, { method :this.options.ajaxmethod, data :data, onComplete : function(r) { if(!this.runPlugins('onCompleteSaveGroupsToDb', null)){ return; } this.form.getElement('input[name=format]').value = orig; this.form.getElement('input[name=task]').value = origprocess; if (this.options.postMethod == 'ajax') { oPackage.sendMessage(this.getBlock(), 'updateRows', 'ok', json); } oPackage.stopLoading(this.getBlock()); }.bind(this) }).request(); }, changePage : function(dir) { if(!this.runPlugins('onChangePage', null)){ return; } this.currentPage = this.currentPage.toInt(); // hide all error messages ($$$ rob why would we want to do that? - commneting out) //this.form.getElements('.fabrikError').addClass('fabrikHide'); if (this.currentPage + dir >= 0 && this.currentPage + dir < this.options.pages.keys().length) { this.currentPage = this.currentPage + dir; if (!this.pageGroupsVisible()){ this.changePage(dir); } } this.setPageButtons(); $('page_' + this.currentPage).setStyle('display', ''); this.hideOtherPages(); if(!this.runPlugins('onEndChangePage', null)){ return; } }, pageGroupsVisible: function() { var visible = false; this.options.pages.get(this.currentPage).each(function(gid){ if ($('group'+gid).getStyle('display') != 'none'){ visible = true; } }); return visible; }, /** * hide all groups except those in the active page */ hideOtherPages:function(){ this.options.pages.each( function(gids, i) { if (i != this.currentPage) { $('page_' + i).setStyle('display', 'none'); } }.bind(this)); }, setPageButtons : function() { if (this.currentPage == this.options.pages.keys().length - 1) { if ($('fabrikSubmit' + this.id)) $('fabrikSubmit' + this.id).disabled = ""; this.form.getElement('.fabrikPageNext').disabled = "disabled"; this.form.getElement('.fabrikPageNext').setStyle('opacity', 0.5); } else { this.form.getElement('.fabrikPageNext').disabled = ""; this.form.getElement('.fabrikPageNext').setStyle('opacity', 1); } if (this.currentPage === 0) { this.form.getElement('.fabrikPagePrevious').disabled = "disabled"; this.form.getElement('.fabrikPagePrevious').setStyle('opacity', 0.5); } else { this.form.getElement('.fabrikPagePrevious').disabled = ""; this.form.getElement('.fabrikPagePrevious').setStyle('opacity', 1); } }, addElements : function(a) { a = $H(a); a.each(function(elements, gid){ elements.each(function(el){ if ($type(el) !== false) { this.addElement(el, el.options.element, gid); } }.bind(this)); }.bind(this)); }, addElement: function(oEl, elId, gid){ elId = elId.replace('[]', ''); oEl.form = this; oEl.groupid = gid; this.formElements.set(elId, oEl); try { oEl.attachedToForm(); } catch (err) { } }, // we have to buffer the events in a pop up window as // the dom inserted when the window loads appears after the ajax evalscripts dispatchEvent : function(elementType, elementId, action, js) { if (!this.options.delayedEvents) { var el = this.formElements.get(elementId); if (el && js != '') { el.addNewEvent(action, js); } } else { this.bufferEvent(elementType, elementId, action, js); } }, bufferEvent : function(elementType, elementId, action, js) { this.bufferedEvents.push( [ elementType, elementId, action, js ]); }, // call this after the popup window has loaded processBufferEvents : function() { this.setUp(); this.options.delayedEvents = false; this.bufferedEvents.each( function(r) { // refresh the element ref var elementId = r[1]; var el = this.formElements.get(elementId); el.element = $(elementId); this.dispatchEvent(r[0], elementId, r[2], r[3]); }.bind(this)); }, action : function(task, element) { var oEl = this.formElements.get(el); eval('oEl.' + task + '()'); }, triggerEvents: function(el){ this.formElements.get(el).fireEvents(arguments[1]); }, /** * @param string * element id to observe * @param string * error div for element * @param string * parent element id - eg for datetime's time field this is the date * fields id */ watchValidation : function(id, triggerEvent) { if (this.options.ajaxValidation == false) { return; } if ($(id).className == 'fabrikSubElementContainer') { // check for things like radio buttons & checkboxes $(id).getElements('.fabrikinput').each( function(i) { i.addEvent(triggerEvent, this.doElementValidation.bindAsEventListener(this, [true])); }.bind(this)); return; } $(id).addEvent(triggerEvent, this.doElementValidation.bindAsEventListener(this, [false])); }, // as well as being called from watchValidation can be called from other // element js actions, e.g. date picker closing doElementValidation : function(event, subEl, replacetxt) { if (this.options.ajaxValidation == false) { return; } replacetxt = $type(replacetxt) == false ? '_time' : replacetxt; if ($type(event) == 'event' || $type(event) == 'object') { // type object in var e = new Event(event); var id = e.target.id; // for elements with subelements eg checkboxes radiobuttons if (subEl == true) { id = $(e.target).findClassUp('fabrikSubElementContainer').id; } } else { // hack for closing date picker where it seems the event object isnt // available id = event; } // for elements with subelements eg checkboxes radiobuttons /*if (subEl == true) { id = $(e.target).findClassUp('fabrikSubElementContainer').id; }*/ if($type($(id)) === false){ return; } if($(id).getProperty('readonly') === true || $(id).getProperty('readonly') == 'readonly'){ //stops date element being validated //return; } var el = this.formElements.get(id); if (!el) { //silly catch for date elements you cant do the usual method of setting the id in the //fabrikSubElementContainer as its required to be on the date element for the calendar to work id = id.replace(replacetxt, ''); el = this.formElements.get(id); if(!el){ return; } } if(!this.runPlugins('onStartElementValidation', event)){ return; } el.setErrorMessage(this.lang.validating, 'fabrikValidating'); var d = $H(this.getFormData()); d.set('controller', 'form'); d.set('task', 'ajax_validate'); d.set('fabrik_postMethod', 'ajax'); d.set('format', 'raw'); d = this._prepareRepeatsForAjax(d); var origid = el.origId ? el.origId : id; el.options.repeatCounter = el.options.repeatCounter ? el.options.repeatCounter : 0; var url = this.options.liveSite + 'index.php?option=com_fabrik&form_id=' + this.id; var myAjax = new Ajax(url, { method :this.options.ajaxmethod, data :d, onComplete :this._completeValidaton.bindAsEventListener(this, [ id, origid ]) }).request(); }, _completeValidaton : function(r, id, origid) { r = Json.evaluate(r); if(!this.runPlugins('onCompleteElementValidation', null)){ return; } var el = this.formElements.get(id); if ($defined(r.modified[origid])) { el.update(r.modified[origid]); } if ($type(r.errors[origid]) !== false) { this._showElementError(r.errors[origid][el.options.repeatCounter], id); } else { this._showElementError([], id); } }, _prepareRepeatsForAjax : function(d) { this.getForm(); //ensure we are dealing with a simple object if ($type(d) === 'hash' || ($type(d.obj) === 'object' && this.options.mooversion == 1.1)) { d = (this.options.mooversion == 1.1) ? d.obj : d.getClean(); } //data should be key'd on the data stored in the elements name between []'s which is the group id this.form.getElements('input[name^=fabrik_repeat_group]').each( function(e) { var c = e.name.match(/\[(.*)\]/)[1]; d['fabrik_repeat_group[' + c + ']'] = e.getValue(); // good for mootools 1.1 } ); return d; }, _showGroupError : function(r, d) { var gids = $A(this.options.pages.get(this.currentPage.toInt())); var err = false; $H(d).each( function(v, k) { k = k.replace('[]', '');//for dropdown validations if(this.formElements.hasKey(k)){ var el = this.formElements.get(k); if(gids.contains(el.groupid)){ if (r.errors[k]) { // prepare error so that it only triggers for real errors and not sucess // msgs var msg = ''; if ($type(r.errors[k]) !== false) { for ( var i = 0; i < r.errors[k].length; i++) { if (r.errors[k][i] != '') { msg += r[i] + '
    '; } } } if (msg !== '') { tmperr = this._showElementError(r.errors[k], k); if (err == false) { err = tmperr; } } } if (r.modified[k]) { if (el) { el.update(r.modified[k]); } } } } }.bind(this)); return err; }, _showElementError : function(r, id) { // r should be the errors for the specific element, down to its repeat group // id. var msg = ''; if ($type(r) !== false) { for ( var i = 0; i < r.length; i++) { if (r[i] != '') { msg += r[i] + '
    '; } } } var classname = (msg === '') ? 'fabrikSuccess' : 'fabrikError'; if (msg === '') msg = this.lang.success; this.formElements.get(id).setErrorMessage(msg, classname); return (classname === 'fabrikSuccess') ? false : true; }, updateMainError : function() { var mainEr = this.form.getElement('.fabrikMainError'); mainEr.setHTML(this.options.error); var activeValidations = this.form.getElements('.fabrikError').filter( function(e, index) { return !e.hasClass('fabrikMainError'); }); if (activeValidations.length > 0 && mainEr.hasClass('fabrikHide')) { mainEr.removeClass('fabrikHide'); var myfx = new Fx.Style(mainEr, 'opacity', { duration :500 }).start(0, 1); } if (activeValidations.length === 0) { myfx = new Fx.Style(mainEr, 'opacity', { duration :500, onComplete : function() { mainEr.addClass('fabrikHide'); } }).start(1, 0); } }, runPlugins : function(func, event) { var ret = true; this.options.plugins.each( function(plugin) { if ($type(plugin[func]) != false) { if (plugin[func](event) == false) { ret = false; } } }); return ret; }, watchSubmit : function() { if (!$('fabrikSubmit' + this.id)) { return; } $('fabrikSubmit' + this.id).addEvent('click', function(e) { var ret = this.runPlugins('onSubmit', e); this.elementsBeforeSubmit(e); if (ret == false) { new Event(e).stop(); // update global status error this.updateMainError(); } if (ret && this.options.postMethod == 'ajax') { //do ajax val only if onSubmit val ok if (this.form) { oPackage.startLoading(this.getBlock()); this.elementsBeforeSubmit(e); // get all values from the form var data = $H(this.getFormData()); data = this._prepareRepeatsForAjax(data); data.fabrik_postMethod = 'ajax'; data.format = 'raw'; var myajax = new Ajax(this.form.action, { 'data' :data, 'method' :this.options.ajaxmethod, onComplete : function(json) { json = Json.evaluate(json); if($type(json) === false) { // stop spinner oPackage.stopLoading(this.getBlock(), 'error in returned json'); return; } // process errors if there are some var errfound = false; if ($defined(json.errors)) { // for every element of the form update error message $H(json.errors).each(function(errors, key){ //replace join[id][label] with join___id___label key = key.replace(/(\[)|(\]\[)/g, '___').replace(/\]/, ''); if(this.formElements.hasKey(key) && errors.flatten().length > 0){ errfound = true; this._showElementError(errors, key); }; }.bind(this)); // this.runPlugins('onAjaxSubmitComplete'); don't run it I guess } // update global status error this.updateMainError(); if (errfound === false) { // $$$ rob clearForm() was commented out but in module with ajax on this gave appearance that // form was not submitted this.clearForm(); oPackage.sendMessage(this.getBlock(), 'updateRows', 'ok', json, this.lang.form_saved); this.runPlugins('onAjaxSubmitComplete', e); }else{ // stop spinner oPackage.stopLoading(this.getBlock(), this.lang.validation_error); } }.bind(this) }).request(); } new Event(e).stop(); } }.bind(this)); }, elementsBeforeSubmit : function(e) { e = new Event(e); this.formElements.each( function(el, key) { if (!el.onsubmit()) { e.stop(); } }); }, // used to get the querystring data and // for any element overwrite with its own data definition // required for empty select lists which return undefined as their value if no // items // available getFormData : function() { this.getForm(); var s = this.form.toQueryString(); var h = {}; s = s.split('&'); var arrayCounters = $H({}); s.each( function(p) { p = p.split('='); var k = p[0]; // $$$ rob deal with checkboxes if(k.substring(k.length-2) === '[]'){ k = k.substring(0, k.length-2); if(!arrayCounters.hasKey(k)){ //rob for ajax validation on repeat element this is required to be set to 0 //arrayCounters.set(k, 1); arrayCounters.set(k, 0); }else{ arrayCounters.set(k, arrayCounters.get(k)+1); } k = k + '[' + arrayCounters.get(k) + ']'; } h[k] = p[1]; }); // $$$rob test commenting out - as this messes up for date from ajax popupform // in cal /* * this.formElements.each(function(el, key){ var v = el.getValue(); if(v !== * false){ h[key] = v; } }.bind(this)); */ return h; }, // $$$ hugh - added this, so far only used by cascading dropdown JS // to populate 'data' for the AJAX update, so custom cascade 'where' clauses // can use {placeholders}. Initially tried to use getFormData for this, but because // it adds ALL the query string args from the page, the AJAX call from cascade ended // up trying to submit the form. So, this func does what the commented out code in // getFormData used to do, and only fecthes actual form element data. getFormElementData : function() { var h = {}; this.formElements.each(function(el,key) { if (el.element) { h[key] = el.getValue(); h[key+'_raw'] = h[key]; } }.bind(this)); return h; }, watchGroupButtons : function() { this.unwatchGroupButtons(); this.form.getElements('.deleteGroup').each( function(g, i) { g.addEvent('click', this.clickDeleteGroup); }.bind(this)); this.form.getElements('.addGroup').each( function(g, i) { g.addEvent('click', this.clickDuplicateGroup); }.bind(this)); this.form.getElements('.fabrikSubGroup').each( function(subGroup) { var r = subGroup.getElement('.fabrikGroupRepeater'); if (r) { subGroup.addEvent('mouseenter', function(e) { r.effect('opacity', { wait :true, duration :200 }).start(0.2, 1); }); subGroup.addEvent('mouseleave', function(e) { r.effect('opacity', { wait :true, duration :200 }).start(1, 0.2); }); } }); }, unwatchGroupButtons : function() { this.form.getElements('.deleteGroup').each( function(g, i) { g.removeEvent('click', this.clickDeleteGroup); }.bind(this)); this.form.getElements('.addGroup').each( function(g, i) { g.removeEvent('click', this.clickDuplicateGroup); }.bind(this)); this.form.getElements('.fabrikSubGroup').each( function(subGroup) { subGroup.removeEvents('mouseenter'); subGroup.removeEvents('mouseleave'); }); }, addGroupJS : function(groupId, e, js) { if (e == 'delete') { this.delGroupJS.set(groupId, js); } else { this.duplicateGroupJS.set(groupId, js); } }, deleteGroup : function(event) { if(!this.runPlugins('onDeleteGroup', event)){ return; } var e = new Event(event).stop(); var group = $(e.target).findClassUp('fabrikGroup'); var i = group.id.replace('group', ''); this.duplicatedGroups.remove(i); if($('fabrik_repeat_group_' + i + '_counter').value == '0'){ return; } var subgroups = group.getElements('.fabrikSubGroup'); var subGroup = $(e.target).findClassUp('fabrikSubGroup'); this.subGroups.set(i, subGroup.clone()); if (subgroups.length <= 1) { this.hideLastGroup(i, subGroup); }else{ var toel = subGroup.getPrevious(); var js = this.delGroupJS.get(i); var myFx = new Fx.Style(subGroup, 'opacity', { duration :300, onComplete : function() { if (subgroups.length > 1) { subGroup.remove(); } this.formElements.each(function(e, k){ if($type($(e.element.id)) == false){ e.decloned(i); this.formElements.remove(k); } }.bind(this)); eval(js); }.bind(this) }).start(1, 0); if (toel) { this.winScroller.toElement(toel); } } // update the hidden field containing number of repeat groups $('fabrik_repeat_group_' + i + '_counter').value = $( 'fabrik_repeat_group_' + i + '_counter').getValue().toInt() - 1; }, hideLastGroup:function(groupid, subGroup){ var sge = subGroup.getElement('.fabrikSubGroupElements'); sge.setStyle('display', 'none'); new Element('div', { 'class' :'fabrikNotice' }).appendText(this.lang.nodata).injectAfter(sge); }, isFirstRepeatSubGroup:function(group) { var subgroups = group.getElements('.fabrikSubGroup'); return subgroups.length == 1 && subgroups[0].getElement('.fabrikNotice'); }, getSubGroupToClone:function(groupid) { var group = $('group' + groupid); var subgroup = group.getElement('.fabrikSubGroup'); if (!subgroup) { subgroup = this.subGroups.get(groupid); } var clone = null; var found = false; if (this.duplicatedGroups.hasKey(groupid)) { found = true; } if (!found) { clone = subgroup.cloneNode(true); this.duplicatedGroups.set(groupid, clone); } else { if (!subgroup) { clone = this.duplicatedGroups.get(groupid); } else { clone = subgroup.cloneNode(true); } } return clone; }, repeatGetChecked:function(group) { ///stupid fix for radio buttons loosing their checked value var tocheck = []; group.getElements('.fabrikinput').each(function(i){ if(i.type == 'radio' && i.getProperty('checked') ){ tocheck.push(i); } }); return tocheck; }, /* duplicates the groups sub group and places it at the end of the group */ duplicateGroup : function(event) { if(!this.runPlugins('onDuplicateGroup', event)){ return; } if (this.options.mooversion == '1.1' && event){ var event = new Event(event); } if (event) event.stop(); var i = $(event.target).findClassUp('fabrikGroup').id.replace('group', ''); var js = this.duplicateGroupJS.get(i); var group = $('group' + i); var c = this.repeatGroupMarkers.get(i); $('fabrik_repeat_group_' + i + '_counter').value = $( 'fabrik_repeat_group_' + i + '_counter').getValue().toInt() + 1; if (this.isFirstRepeatSubGroup(group)) { var subgroups = group.getElements('.fabrikSubGroup'); //user has removed all repeat groups and now wants to add it back in //remove the 'no groups' notice subgroups[0].getElement('.fabrikNotice').remove(); subgroups[0].getElement('.fabrikSubGroupElements').setStyle('display', ''); return; } var clone = this.getSubGroupToClone(i); var tocheck = this.repeatGetChecked(group); group.appendChild(clone); tocheck.each(function(i){ i.setProperty('checked', true); }); // remove values and increment ids var newElementControllers = []; this.subelementCounter = 0; var hasSubElements = false; var inputs = clone.getElements('.fabrikinput'); var lastinput = null; this.formElements.each( function(el) { var formElementFound = false; subElementContainer = null; var subElementCounter = -1; inputs.each( function(input) { hasSubElements = el.hasSubElements(); //for all instances of the call to findClassUp use el.element rather than input (HMM SEE LINE 912 - PERHAPS WE CAN REVERT TO USING INPUT NOW?) // var testid = (hasSubElements) ? // input.findClassUp('fabrikSubElementContainer').id : input.id //var testid = (hasSubElements) ? el.element.findClassUp('fabrikSubElementContainer').id : input.id; var testid = (hasSubElements) ? input.findClassUp('fabrikSubElementContainer').id : input.id; if (el.options.element == testid) { lastinput = input; formElementFound = true; if (hasSubElements) { subElementCounter++; //the line below meant that we updated the orginal groups id @ line 942 - which in turn meant when we cleared the values we were clearing the orignal elements values //not sure how this fits in with comments above which state we should use el.element.findClassUp('fabrikSubElementContainer'); //REAL ISSUE WAS THAT inputs CONTAINED ADD OPTIONS (elementmodel->getAddOptionFields) WHICH HAD ELEMENTS WITH THE CLASS fabrikinput THIS CLASS IS RESERVERED FOR ACTUAL DATA ELEMENTS //subElementContainer = el.element.findClassUp('fabrikSubElementContainer'); subElementContainer = input.findClassUp('fabrikSubElementContainer'); // clone the first inputs event to all subelements input.cloneEvents($(testid).getElement('input')); // id set out side this each() function } else { input.cloneEvents(el.element); // update the element id use el.element.id rather than input.id as that may contain _1 at end of id input.id = el.element.id + '_' + c; // update labels for non sub elements var l = input.findClassUp('fabrikElementContainer').getElement('label'); if (l) { l.setProperty('for', input.id); } } input.name = input.name.replace('[0]', '[' + (c) + ']'); } }.bind(this)); if (formElementFound) { if (hasSubElements && $type(subElementContainer) != false ) { // if we are checking subelements set the container id after they have all // been processed // otherwise if check only works for first subelement and no further // events are cloned subElementContainer.id = el.options.element + '_' + c; } var origelid = el.options.element; // clone js element controller, set form to be passed by reference and not cloned var ignore = el.unclonableProperties(); var newEl = new CloneObject(el, true, ignore); newEl.container = null; newEl.options.repeatCounter = c; newEl.origId = origelid; if (hasSubElements && $type(subElementContainer) != false ) { newEl.element = $(subElementContainer); newEl.options.element = subElementContainer.id; newEl._getSubElements(); } else { newEl.element = $(lastinput.id); newEl.options.element = lastinput.id; } newEl.reset(); newElementControllers.push(newEl); } }.bind(this)); // add new element controllers to form this.addElements({i:newElementControllers}); this.winScroller.toElement(clone); var myFx = new Fx.Style(clone, 'opacity', { duration :500 }).set(0); newElementControllers.each( function(newEl) { newEl.cloned(c); }); c = c + 1; myFx.start(1); eval(js); this.repeatGroupMarkers.set(i, this.repeatGroupMarkers.get(i) + 1); this.unwatchGroupButtons(); this.watchGroupButtons(); }, update:function(o){ if(!this.runPlugins('onUpdate', null)){ return; } var leaveEmpties = arguments[1] || false; var data = o.data; this.getForm(); if (this.form) { // test for detailed view in module??? var rowidel = this.form.getElement('input[name=rowid]'); if (rowidel && data.rowid) { rowidel.value = data.rowid; } } this.formElements.each( function(el, key) { if (key.substring(key.length - 3, key.length) == '_ro') { key = key.substring(0, key.length - 3); } // this if stopped the form updating empty fields. Element update() methods // should test for null // variables and convert to their correct values // if (data[key]) { if ($type(data[key]) === false) { // only update blanks if the form is updating itself // leaveEmpties set to true when this form is called from updateRows if (o.id == this.id && !leaveEmpties) { el.update(''); } } else { el.update(data[key]); } }.bind(this)); }, reset : function() { if(!this.runPlugins('onReset', null)){ return; } this.formElements.each( function(el, key) { el.reset(); }.bind(this)); }, showErrors : function(data) { var d = null; if (data.id == this.id) { // show errors var errors = new Hash(data.errors); if (errors.keys().length > 0) { if($type(this.form.getElement('.fabrikMainError')) !== false){ this.form.getElement('.fabrikMainError').setHTML(this.options.error); this.form.getElement('.fabrikMainError').removeClass('fabrikHide'); } errors.each( function(a, key) { if ($(key + '_error')) { var e = $(key + '_error'); var msg = new Element('span'); for ( var x = 0; x < a.length; x++) { for ( var y = 0; y < a[x].length; y++) { d = new Element('div').appendText(a[x][y]).injectInside(e); } } } else { fconsole(key + '_error' + ' not found'); } }); } } }, /** add additional data to an element - e.g database join elements */ appendInfo : function(data) { this.formElements.each( function(el, key) { if (el.appendInfo) { el.appendInfo(data); } }.bind(this)); }, addListenTo : function(blockId) { this.listenTo.push(blockId); }, clearForm : function() { this.getForm(); if (!this.form) { return; } this.formElements.each( function(el, key) { if (key == this.options.primaryKey) { this.form.getElement('input[name=rowid]').value = ''; } el.update(''); }.bind(this)); // reset errors this.form.getElements('.fabrikError').empty(); this.form.getElements('.fabrikError').addClass('fabrikHide'); }, receiveMessage : function(senderBlock, task, taskStatus, data) { if (this.listenTo.indexOf(senderBlock) != -1) { if (task == 'processForm') { } // a row from the table has been loaded if (task == 'update') { this.update(data); } if (task == 'clearForm') { this.clearForm(); } } // a form has been submitted which contains data that should be updated in this // form // currently for updating database join drop downs, data is used just as a // test to see if the dd needs // updating. If found a new ajax call is made from within the dd to update // itself // $$$ hugh - moved showErrors() so it only runs if data.errors has content if (task == 'updateRows') { if ($H(data.errors).keys().length === 0) { if ($type(data.data) !== false) { this.appendInfo(data); this.update(data, true); } } else { this.showErrors(data); } } }, addPlugin : function(plugin) { this.options.plugins.push(plugin); } }); /** * @author Robert */ var FbElement = new Class({ initialize: function(element, options) { this.plugin = ''; this.strElement = element; this.setOptions(element, options); }, attachedToForm: function() { //put ini code in here that can't be put in initialize() // generally any code that needs to refer to this.form, which //is only set when the element is assigned to the form. }, setOptions: function(element, options) { if($(element)){ this.element = $(element); } this.options = { element: element, defaultVal: '', value:'', editable:false }; Object.extend(this.options, options || {}); this.setorigId(); }, /** allows you to fire an array of events to element / subelements, used in calendar to trigger js events when the calendar closes **/ fireEvents: function(evnts){ if(this.hasSubElements()){ this.subElements.each(function(el){ $A(evnts).each(function(e){ el.fireEvent(e); }.bind(this)); }.bind(this)); }else{ $A(evnts).each(function(e){ this.element.fireEvent(e); }.bind(this)); } }, getElement: function() { //use this in mocha forms whose elements (such as database jons) arent loaded //when the class is ini'd if($type(this.element) === false){ this.element = $(this.options.element); } return this.element; }, //used for elements like checkboxes or radio buttons _getSubElements: function(){ var element = this.getElement(); if($type(element) === false){ return false; } this.subElements = element.getElements('.fabrikinput'); return this.subElements; }, hasSubElements: function(){ this._getSubElements(); if($type(this.subElements) === 'array'){ return this.subElements.length > 0 ? true : false; } return false; }, unclonableProperties:function() { return ['form']; }, addNewEvent: function( action, js ){ if(action == 'load'){ if ($type(js) === 'function') { js.delay(0); } else { eval(js); } }else{ if(!this.element){ this.element = $(this.strElement); } if(this.element){ this.element.addEvent( action, function(e){ e = new Event(e).stop(); if ($type(js) === 'function') { js.delay(0); } else { eval(js); } } ); this.element.addEvent('blur', function(e){ this.validate(); }.bind(this)); } } }, validate:function(){}, //store new options created by user in hidden field addNewOption: function(val, label) { var added = $(this.options.element + '_additions').value; var json = {'val':val,'label':label}; if(added !== ''){ var a = Json.evaluate(added); }else{ a = []; } a.push(json); var s = '['; for(var i=0;i 0){ var e = this.options.element; this.origId = e.substring(0, e.length - 1 - this.options.repeatCounter.toString().length); } } }); FbElement.implement(new Events); /** * @author Rob * contains methods that are used by any element which manipulates files/folders */ var FbFileElement = FbElement.extend({ ajaxFolder: function() { this.folderlist = []; var el = this.element.findClassUp('fabrikElement'); this.breadcrumbs = el.getElement('.breadcrumbs'); this.folderdiv = el.getElement('.folderselect'); this.slider = new Fx.Slide(this.folderdiv , {duration: 500}); this.slider.hide(); this.hiddenField = el.getElement('.folderpath'); el.getElement('.toggle').addEvent('click', function(e){ new Event(e).stop(); this.slider.toggle(); }.bind(this)) this.watchAjaxFolderLinks(); }, watchAjaxFolderLinks: function() { this.folderdiv.getElements('a').addEvent('click', this.browseFolders.bindAsEventListener(this)); this.breadcrumbs.getElements('a').addEvent('click', this.useBreadcrumbs.bindAsEventListener(this)); }, browseFolders: function(e){ e = new Event(e).stop(); var a = $(e.target); this.folderlist.push(a.innerHTML); var dir = this.options.dir + this.folderlist.join(this.options.ds); this.addCrumb(a.innerHTML); this.doAjaxBrowse(dir); }, useBreadcrumbs: function(e) { e = new Event(e).stop(); var found = false; var a = $(e.target); var c = a.className; this.folderlist = []; var res = this.breadcrumbs.getElements('a').every(function(link){ if(link.className == a.className){ return false; } this.folderlist.push(a.innerHTML); return true; }, this); var home = [this.breadcrumbs.getElements('a').shift().clone(), this.breadcrumbs.getElements('span').shift().clone()]; this.breadcrumbs.empty(); this.breadcrumbs.adopt(home); this.folderlist.each(function(txt){ this.addCrumb(txt); }, this); var dir = this.options.dir + this.folderlist.join(this.options.ds); this.doAjaxBrowse(dir); }, doAjaxBrowse: function( dir ){ var url = this.options.liveSite+"index.php?option=com_fabrik&format=raw&controller=plugin&task=pluginAjax&plugin=fabrikfileupload&method=ajax_getFolders&element_id="+ this.options.id; new Ajax(url, { data:{'dir':dir}, onComplete:function(r){ r = Json.evaluate(r); this.folderdiv.empty(); r.each(function(folder){ new Element('li', { 'class':'fileupload_folder'}).adopt( new Element('a', {'href':'#'}).setText(folder)).injectInside(this.folderdiv); }.bind(this)); if (r.length == 0){ this.slider.hide(); }else{ this.slider.slideIn(); } this.watchAjaxFolderLinks(); this.hiddenField.value = '/'+this.folderlist.join('/') + '/'; this.fireEvent('onBrowse'); }.bind(this) }).request(); }, addCrumb:function(txt){ this.breadcrumbs.adopt( new Element('a', {'href':'#', 'class':'crumb'+ this.folderlist.length}).setText(txt), new Element('span').setText(' / ') ); } });var fbField = FbElement.extend({ initialize: function(element, options) { this.plugin = 'fabrikfield'; this.setOptions(element, options); } });var fbDatabasejoin = FbElement.extend({ initialize: function(element, options) { this.plugin = 'fabrikdatabasejoin'; this.options = Object.extend({ 'liveSite':'', 'popupform':49, 'id':0, 'formid':0, 'key':'', 'label':'', 'popwiny':0, 'windowwidth':360, 'displayType':'dropdown' }, options || {}); this.setOptions(element, this.options); //if users can add records to the database join drop down if($(element + '_add')){ this.startEvent = this.start.bindAsEventListener(this); $(element + '_add').addEvent('click', this.startEvent); //register the popup window with the form this element is in //do this so that the database join drop down can be updated oPackage.bindListener('form_' + this.options.popupform, 'form_' + this.options.formid); } if($(element + '_select')){ $(element + '_select').addEvent('click', this.selectRecord.bindAsEventListener(this)); //register the popup window with the form this element is in //do this so that the database join drop down can be updated oPackage.bindListener('table_' + this.options.tableid, 'form_' + this.options.formid); } }, selectRecord: function(e){ e = new Event(e).stop(); var id = this.element.id + '-popupwin'; var url = this.options.liveSite + "index.php?option=com_fabrik&view=table&tmpl=component&layout=dbjoinselect&_postMethod=ajax&tableid=" + this.options.tableid; url += "&triggerElement="+this.element.id; url += "&winid="+id; this.windowopts = { 'id': id, title: 'Select', contentType: 'xhr', loadMethod: 'xhr', contentURL: url, width: this.options.windowwidth.toInt(), height: 320, y: this.options.popwiny, 'minimizable': false, 'collapsible': true, onContentLoaded: function(){ oPackage.resizeMocha(id); } }; if(this.options.mooversion > 1.1){ var mywin = new MochaUI.Window(this.windowopts); }else{ document.mochaDesktop.newWindow(this.windowopts); } }, getValue:function(){ this.getElement(); if(!this.options.editable){ return this.options.value; } if($type(this.element) === false){ return ''; } if(this.options.display_type != 'dropdown') { var v = ''; this._getSubElements().each(function(sub){ if(sub.checked){ v = sub.getValue(); return v; } return null; }); return v; } if($type(this.element.getValue()) === false){ return ''; } return this.element.getValue(); }, start: function(event){ var e = new Event(event); var url = this.options.liveSite + "index.php?option=com_fabrik&view=form&tmpl=component&_postMethod=ajax&fabrik=" + this.options.popupform; var id = this.element.id + '-popupwin'; url += "&winid="+id; this.windowopts = { 'id': id, title: 'Add', contentType: 'xhr', loadMethod:'xhr', contentURL: url, width: this.options.windowwidth.toInt(), height: 320, y:this.options.popwiny, 'minimizable':false, 'collapsible':true, onContentLoaded: function(){ oPackage.resizeMocha(id); } }; if(this.options.mooversion > 1.1){ this.win = new MochaUI.Window(this.windowopts); }else{ document.mochaDesktop.newWindow(this.windowopts); } e.stop(); }, update:function(val){ this.getElement(); if($type(this.element) === false){ return; } if (!this.options.editable) { this.element.innerHTML = ''; if(val === ''){ return; } val = val.split(this.options.splitter); //was a security issue as options.data contained unaccessibly element data //var h = $H(this.options.data); var h = this.form.getFormData(); val.each(function(v){ this.element.innerHTML += h.get(v) + "
    "; }.bind(this)); return; } this.setValue(val); }, setValue:function(val){ if($type(this.element.options) !== false) { //needed with repeat group code for (var i = 0; i < this.element.options.length; i++) { if (this.element.options[i].value == val) { this.element.options[i].selected = true; break; } } } this.options.value = val; }, // appendInfo: function(data){ var opts = []; if(data === '' || $type(data.data) === false){ return; } var key = this.options.key; var label = this.options.label; data = data.data; outerLoop: for(var i=0;i 1.1) { //@TODO this isnt working in IE8 - some issue with mt element.retrive() method if(this.options.mooversion == 1.2){ id = $(id); } MochaUI.closeWindow(id); } else { document.mochaDesktop.closeWindow(id); } } }, cloned: function(c){ //@TODO this is going to wipe out any user added change events to the element // cant' figure out how to just remove the cdd change events. this.element.removeEvents('change'); //c is the repeat group count }, addNewEvent: function( action, js ){ if (this.options.displayType == 'dropdown') { if (this.element) { this.element.addEvent(action, function(e){ e = new Event(e).stop(); ($type(js) === 'function') ? js.delay(0) : eval(js); }); } } else { if (action == 'load') { eval(js); } else { this._getSubElements(); this.subElements.each(function(el){ el.addEvent(action, function(e){ ($type(js) === 'function')?js.delay(0):eval(js); }); }); } } } });var fbUser = fbDatabasejoin.extend({});var fbDropdown = FbElement.extend({ initialize: function(element, options) { this.plugin = 'fabrikdropdown'; this.setOptions(element, options); this.lang = Object.extend({ please_enter_value:'Please enter a value and/or label' }, arguments[2] || {}); if(this.options.allowadd === true && this.options.editable !== false){ var id = this.element.id; $( this.element.id + '_dd_add_entry').addEvent( 'click', function(event){ var label = $(id + '_ddLabel').value; if($( id + '_ddVal')){ var val = $( id + '_ddVal').value; }else{ val = label; } if (val === '' || label === '') { alert(this.lang.please_enter_value); } else { var opt = new Element('option', { 'selected':'selected', 'value': val }).appendText(label).injectInside($(this.element.id)); var e = new Event(event).stop(); if ($(id + '_ddVal')) { $(id + '_ddVal').value = ''; } $(id + '_ddLabel').value = ''; this.addNewOption(val, label); } }.bind(this)); } }, getValue:function(){ if(!this.options.editable){ return this.options.value; } if($type(this.element.getValue()) === false){ return ''; } return this.element.getValue(); }, reset: function() { var v = this.options.defaultVal.join(this.options.splitter); this.update(v); }, update: function(val){ if($type(val) == 'string'){ val = val.split(this.options.splitter); } if($type(val) == false){ val = []; } this.getElement(); if($type(this.element) === false){ return; } if (!this.options.editable) { this.element.innerHTML = ''; var h = $H(this.options.data); val.each(function(v){ this.element.innerHTML += h.get(v) + "
    "; }.bind(this)); return; } for (var i = 0; i < this.element.options.length; i++) { if (val.indexOf(this.element.options[i].value) != -1 ){ this.element.options[i].selected = true; }else{ this.element.options[i].selected = false; } } } });var fbTextarea = FbElement.extend({ initialize: function(element, options) { this.plugin = 'fabriktextarea'; this.elementtype_id = 'fabrikDisplayText'; this.setOptions(element, options); this.getTextContainer(); this.watchTextContainer(); }, watchTextContainer: function() { if($type(this.element) === false){ this.element = $(this.options.element); } if($type(this.element) === false){ //can occur when element is part of hidden first group return ; } if(this.options.editable == true) { if ($type(this.element.findClassUp('fabrikElementContainer'))== false){ fconsole('no fabrikElementContainer class found for textarea'); return; } var element = this.element.findClassUp('fabrikElementContainer').getElement('.fabrik_characters_left'); if($type(element) !== false){ this.warningFX = element.effects({duration: 1000, transition: Fx.Transitions.Quart.easeOut}); this.origCol = element.getStyle('color'); if (this.options.wysiwyg) { var eventHandler = this.informKeyPress.bindAsEventListener(this); if (this.options.tinymce != 3) { tinyMCE.addEvent(this.container, "keydown", eventHandler); } else { tinymce.dom.Event.add(this.container, 'keydown', eventHandler); } }else{ this.container.addEvent('keydown', function(e){ this.informKeyPress(); }.bind(this)); } } } }, cloned: function(c){ //c is the repeat group count this.getTextContainer(); this.watchTextContainer(); }, getTextContainer: function() { if (this.options.wysiwyg) { if (this.options.tinymce != 3) { var instance = tinyMCE.getInstanceById(this.options.element); } else { var instance = tinyMCE.get(this.options.element); } if(instance){ this.container = instance.getDoc(); }else{ fconsole('didnt find wysiwyg edtor ...' + this.options.element); } }else{ this.container = this.element; } }, getContent:function() { if (this.options.wysiwyg) { if (this.options.tinymce != 3) { return tinyMCE.getContent().replace(/<\/?[^>]+(>|$)/g, ""); } else { return tinyMCE.activeEditor.getContent().replace(/<\/?[^>]+(>|$)/g, ""); } }else{ return this.container.value; } }, setContent: function(c) { if (this.options.wysiwyg) { return tinyMCE.setContent(c); }else{ this.getTextContainer(); if($type(this.container) !== false){ this.container.value = c; } } return null; }, informKeyPress: function() { var charsleftEl = this.element.findClassUp('fabrikElementContainer').getElement('.fabrik_characters_left'); var content = this.getContent(); var charsLeft = this.options.max - (content.length + 1); if(charsLeft < 0){ this.setContent( content.substring(0,this.options.max) ); charsLeft = 0; this.warningFX.start({'opacity':0, 'color':'#FF0000'}).chain(function(){ this.start({'opacity':1, 'color':'#FF0000'}).chain(function(){ this.start( {'opacity':0,'color':this.origCol}).chain(function(){ this.start({'opacity':1}); }); }); }); }else{ charsleftEl.setStyle('color',this.origCol); } charsleftEl.getElement('span').setHTML(charsLeft); }, reset: function() { this.update(this.options.defaultVal); }, update: function(val){ this.getTextContainer(); if (!this.options.editable) { this.element.innerHTML = val; return; } this.setContent(val); } });var fbCaptcha = FbElement.extend({ initialize: function(element, options) { this.setOptions(element, options); } });if(!window.CanvasRenderingContext2D){(function(){var I=Math,i=I.round,L=I.sin,M=I.cos,m=10,A=m/2,Q={init:function(a){var b=a||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var c=this;b.attachEvent("onreadystatechange",function(){c.r(b)})}},r:function(a){if(a.readyState=="complete"){if(!a.namespaces["s"]){a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var b=a.createStyleSheet();b.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}"; var c=a.getElementsByTagName("canvas");for(var d=0;d"){var d="/"+a.tagName,e;while((e=a.nextSibling)&&e.tagName!=d){e.removeNode()}if(e){e.removeNode()}}a.parentNode.replaceChild(c,a);return c},initElement:function(a){a=this.q(a);a.getContext=function(){if(this.l){return this.l}return this.l=new K(this)};a.attachEvent("onpropertychange",V);a.attachEvent("onresize", W);var b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+"px"}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+"px"}else{a.height=a.clientHeight}return a}};function V(a){var b=a.srcElement;switch(a.propertyName){case "width":b.style.width=b.attributes.width.nodeValue+"px";b.getContext().clearRect();break;case "height":b.style.height=b.attributes.height.nodeValue+"px";b.getContext().clearRect();break}}function W(a){var b=a.srcElement; if(b.firstChild){b.firstChild.style.width=b.clientWidth+"px";b.firstChild.style.height=b.clientHeight+"px"}}Q.init();var R=[];for(var E=0;E<16;E++){for(var F=0;F<16;F++){R[E*16+F]=E.toString(16)+F.toString(16)}}function J(){return[[1,0,0],[0,1,0],[0,0,1]]}function G(a,b){var c=J();for(var d=0;d<3;d++){for(var e=0;e<3;e++){var g=0;for(var h=0;h<3;h++){g+=a[d][h]*b[h][e]}c[d][e]=g}}return c}function N(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit= a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.d=a.d;b.e=a.e}function O(a){var b,c=1;a=String(a);if(a.substring(0,3)=="rgb"){var d=a.indexOf("(",3),e=a.indexOf(")",d+1),g=a.substring(d+1,e).split(",");b="#";for(var h=0;h<3;h++){b+=R[Number(g[h])]}if(g.length==4&&a.substr(3,1)=="a"){c=g[3]}}else{b=a}return[b,c]}function S(a){switch(a){case "butt":return"flat";case "round":return"round"; case "square":default:return"square"}}function K(a){this.a=J();this.m=[];this.k=[];this.c=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=m*1;this.globalAlpha=1;this.canvas=a;var b=a.ownerDocument.createElement("div");b.style.width=a.clientWidth+"px";b.style.height=a.clientHeight+"px";b.style.overflow="hidden";b.style.position="absolute";a.appendChild(b);this.j=b;this.d=1;this.e=1}var j=K.prototype;j.clearRect=function(){this.j.innerHTML= "";this.c=[]};j.beginPath=function(){this.c=[]};j.moveTo=function(a,b){this.c.push({type:"moveTo",x:a,y:b});this.f=a;this.g=b};j.lineTo=function(a,b){this.c.push({type:"lineTo",x:a,y:b});this.f=a;this.g=b};j.bezierCurveTo=function(a,b,c,d,e,g){this.c.push({type:"bezierCurveTo",cp1x:a,cp1y:b,cp2x:c,cp2y:d,x:e,y:g});this.f=e;this.g=g};j.quadraticCurveTo=function(a,b,c,d){var e=this.f+0.6666666666666666*(a-this.f),g=this.g+0.6666666666666666*(b-this.g),h=e+(c-this.f)/3,l=g+(d-this.g)/3;this.bezierCurveTo(e, g,h,l,c,d)};j.arc=function(a,b,c,d,e,g){c*=m;var h=g?"at":"wa",l=a+M(d)*c-A,n=b+L(d)*c-A,o=a+M(e)*c-A,f=b+L(e)*c-A;if(l==o&&!g){l+=0.125}this.c.push({type:h,x:a,y:b,radius:c,xStart:l,yStart:n,xEnd:o,yEnd:f})};j.rect=function(a,b,c,d){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath()};j.strokeRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.stroke()};j.fillRect=function(a, b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill()};j.createLinearGradient=function(a,b,c,d){var e=new H("gradient");return e};j.createRadialGradient=function(a,b,c,d,e,g){var h=new H("gradientradial");h.n=c;h.o=g;h.i.x=a;h.i.y=b;return h};j.drawImage=function(a,b){var c,d,e,g,h,l,n,o,f=a.runtimeStyle.width,k=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var q=a.width,r=a.height;a.runtimeStyle.width= f;a.runtimeStyle.height=k;if(arguments.length==3){c=arguments[1];d=arguments[2];h=(l=0);n=(e=q);o=(g=r)}else if(arguments.length==5){c=arguments[1];d=arguments[2];e=arguments[3];g=arguments[4];h=(l=0);n=q;o=r}else if(arguments.length==9){h=arguments[1];l=arguments[2];n=arguments[3];o=arguments[4];c=arguments[5];d=arguments[6];e=arguments[7];g=arguments[8]}else{throw"Invalid number of arguments";}var s=this.b(c,d),t=[],v=10,w=10;t.push(" ','","");this.j.insertAdjacentHTML("BeforeEnd",t.join(""))};j.stroke=function(a){var b=[],c=O(a?this.fillStyle:this.strokeStyle),d=c[0],e=c[1]*this.globalAlpha,g=10,h=10;b.push("n.x){n.x=k.x}if(l.y== null||k.yn.y){n.y=k.y}}}b.push(' ">');if(typeof this.fillStyle=="object"){var v={x:"50%",y:"50%"},w=n.x-l.x,x=n.y-l.y,p=w>x?w:x;v.x=i(this.fillStyle.i.x/w*100+50)+"%";v.y=i(this.fillStyle.i.y/x*100+50)+"%";var y=[];if(this.fillStyle.p=="gradientradial"){var z=this.fillStyle.n/p*100,B=this.fillStyle.o/p*100-z}else{var z=0,B=100}var C={offset:null,color:null},D={offset:null,color:null};this.fillStyle.h.sort(function(T,U){return T.offset-U.offset});for(var o=0;oC.offset||C.offset==null){C.offset=u.offset;C.color=u.color}if(u.offset')}else if(a){b.push('')}else{b.push("')}b.push("");this.j.insertAdjacentHTML("beforeEnd",b.join(""));this.c=[]};j.fill=function(){this.stroke(true)};j.closePath=function(){this.c.push({type:"close"})};j.b=function(a,b){return{x:m*(a*this.a[0][0]+b*this.a[1][0]+this.a[2][0])-A,y:m*(a*this.a[0][1]+b*this.a[1][1]+this.a[2][1])-A}};j.save=function(){var a={};N(this,a); this.k.push(a);this.m.push(this.a);this.a=G(J(),this.a)};j.restore=function(){N(this.k.pop(),this);this.a=this.m.pop()};j.translate=function(a,b){var c=[[1,0,0],[0,1,0],[a,b,1]];this.a=G(c,this.a)};j.rotate=function(a){var b=M(a),c=L(a),d=[[b,c,0],[-c,b,0],[0,0,1]];this.a=G(d,this.a)};j.scale=function(a,b){this.d*=a;this.e*=b;var c=[[a,0,0],[0,b,0],[0,0,1]];this.a=G(c,this.a)};j.clip=function(){};j.arcTo=function(){};j.createPattern=function(){return new P};function H(a){this.p=a;this.n=0;this.o= 0;this.h=[];this.i={x:0,y:0}}H.prototype.addColorStop=function(a,b){b=O(b);this.h.push({offset:1-a,color:b})};function P(){}G_vmlCanvasManager=Q;CanvasRenderingContext2D=K;CanvasGradient=H;CanvasPattern=P})()}; /* ----------------------------------------------------------------- Script: mocha.js v.0.7 Copyright: Copyright (c) 2007 Greg Houston, License: MIT-style license Contributors: Scott F. Frederick Joel Lindau ----------------------------------------------------------------- */ var MochaDesktop = new Class({ options: { draggable: true, resizable: true, minimizable: true, // this is automatically reset to false if there is no dock maximizable: true, // this is automatically reset to false if #mochaDesktop is not present closable: true, headerHeight: 25, footerHeight: 30, cornerRadius: 9, desktopTopOffset: 20, // use a negative number if neccessary to place first window where you want it desktopLeftOffset: 290, mochaTopOffset: 70, // initial vertical spacing of each window mochaLeftOffset: 70, // initial horizontal spacing of each window newWindowPosTop: 0, // In the current setup this just initializes the variable and does not effect the position newWindowPosLeft: 0, // In the current setup this just initializes the variable and does not effect the position minWidth: 250, // minimum width of windows when resized maxWidth: 2500, // maximum width of windows when resized minHeight: 100, // minimum height of windows when resized maxHeight: 2000 // maximum height of windows when resized }, initialize: function(options){ this.setOptions(options); // Private properties this.indexLevel = 1; this.mochaControlsWidth = 0; this.minimizebuttonX = 0; this.maximizebuttonX = 0; this.closebuttonX = 0; this.scrollWidthOffset = 6; this.windowIDCount = 0; new Element('canvas'); // Add properties to elements in the DOM // Consider moving this to windowProperties // so it is not added to all the elements in the DOM // but just to the windows Element.implement({oldTop: ''}); Element.implement({oldLeft: ''}); Element.implement({oldWidth: ''}); Element.implement({oldHeight: ''}); Element.implement({maximizeToggle: 'maximize'}); Element.implement({modal: ''}); Element.implement({iframe: ''}); Element.implement({contentURL: ''}); $$('div.mocha').setStyle('display', 'block'); if ($('mochaDesktop')) { this.setDesktopSize(); } else { this.options.maximizable = false; } if ($('mochaDock')) { if (this.options.minimizable == true){ $('mochaDock').setStyles({ 'position': 'absolute', 'top': null, 'bottom': 0, 'left': 0 }); this.initDock($('mochaDock')); this.drawDock($('mochaDock')); } else { $('mochaDock').setStyle('display', 'none'); } } else { this.options.minimizable = false; } this.insertWindowElements($$('div.mocha')); this.drawAll(); this.attachDraggable($$('div.mocha')); this.attachResizable($$('div.mocha')); this.attachFocus($$('div.mocha')); this.attachMinimize($$('div.mocha')); this.attachMaximize($$('div.mocha')); this.attachClose($$('div.mocha')); this.arrangeCascade(); // Modal initialization var mochaModal = new Element('div', { 'id': 'mochaModalBackground' }); if ($('mochaDesktop')){ mochaModal.injectInside($('mochaDesktop')); } else { mochaModal.injectInside(document.body); } this.setModalSize(); mochaModal.setStyle('opacity', .4); this.modalOpenMorph = new Fx.Styles($('mochaModalBackground'), { 'duration': 200 }); this.modalCloseMorph = new Fx.Styles($('mochaModalBackground'), { 'duration': 200, onComplete: function(){ $('mochaModalBackground').setStyle('display', 'none'); }.bind(this) }); if (window.ie && $("mochaDesktopNavbar")){ // fix for dropdown menus in IE var sfEls = $("mochaDesktopNavbar").getElementsByTagName("LI"); for (var i=0; i