//add js to html element for css selectors document.documentElement.className = 'js'; //global - TR app or bookmarklet var bookmarklet = window.location.href.indexOf('developertool') > -1; //global for tracking open and focused toolbar panels on refresh var openGroups = []; var focusedEl = null; //backbutton and hash bookmarks support var hash = { storedHash: '', currentTabHash: '', //The hash that's only stored on a tab switch cache: '', interval: null, listen: true, // listen to hash changes? // start listening again startListening: function() { setTimeout(function() { hash.listen = true; }, 600); }, // stop listening to hash changes stopListening: function() { hash.listen = false; }, //check if hash has changed checkHashChange: function() { var locStr = hash.currHash(); if(hash.storedHash != locStr) { if(hash.listen == true) hash.refreshToHash(); ////update was made by back button hash.storedHash = locStr; } if(!hash.interval) hash.interval = setInterval(hash.checkHashChange, 500); }, //refresh to a certain hash refreshToHash: function(locStr) { if(locStr) var newHash = true; locStr = locStr || hash.currHash(); updateCSS(locStr); // remember which groups are open openGroups = []; $('div.theme-group-content').each(function(i){ if($(this).is(':visible')){openGroups.push(i);} }); // remember any focused element focusedEl = null; $('form input, form select, form .texturePicker').each(function(i){ if($(this).is('.focus')){ focusedEl = i; } }); // reload tab $('#rollerTabs').tabs('url', 0, '/themeroller/_rollyourown.php?'+ locStr); $('#rollerTabs').tabs('load', 0); // if the hash is passed if(newHash){ hash.updateHash(locStr, true); } }, updateHash: function(locStr, ignore) { if(ignore == true){ hash.stopListening(); } window.location.hash = locStr; if(bookmarklet){ window.parent.location.hash = locStr; } if(ignore == true){ hash.storedHash = locStr; hash.startListening(); } }, clean: function(locStr){ return locStr.replace(/%23/g, "").replace(/[\?#]+/g, ""); }, currHash: function() { return hash.clean(window.location.hash); //return hash.clean(encodeURIComponent(window.location.hash)); }, currSearch: function() { return hash.clean(window.location.search); //return hash.clean(encodeURIComponent(window.location.search)); }, init: function(){ hash.storedHash = ''; hash.checkHashChange(); } }; //function to append a new theme stylesheet with the new style changes function updateCSS(locStr){ //once 1.6 final is ready: $("head").append(''); $("link[href*=parseTheme\\.css\\.php]:last").after(''); if($("link[href*=parseTheme\\.css\\.php]").size() > 3){ $("link[href*=parseTheme\\.css\\.php]:first").remove(); } }; //function called after a change event in the form function formChange(){ var locStr = $('#themeRoller form').serialize(); locStr = hash.clean(locStr); updateCSS(locStr); hash.updateHash(locStr, true); }; //set up spindowns $.fn.spinDown = function() { return this.click(function() { var $this = $(this); $this.next().slideToggle(100); $this.find('.icon').toggleClass('icon-triangle-1-s').end().toggleClass('state-active'); if($this.is('.corner-all')) { $this.removeClass('corner-all').addClass('corner-top'); } else if($this.is('.corner-top')) { $this.removeClass('corner-top').addClass('corner-all'); } return false; }); }; // validation for hex inputs $.fn.validHex = function() { return this.each(function() { var value = $(this).val(); value = value.replace(/[^#a-fA-F0-9]/g, ''); // non [#a-f0-9] value = value.toLowerCase(); if(value.match(/#/g) && value.match(/#/g).length > 1) value = value.replace(/#/g, ''); // ## if(value.indexOf('#') == -1) value = '#'+value; // no # if(value.length > 7) value = value.substr(0,7); // too many chars $(this).val(value); }); }; //color pickers setup (sets bg color of inputs) $.fn.applyFarbtastic = function() { return this.each(function() { $('
').farbtastic(this).remove(); }); }; // events within the 'roll your own' tab function rollYourOwnBehaviors() { // hover class toggles in app panel $('li.state-default, div.state-default').hover( function(){ $(this).addClass('state-hover'); }, function(){ $(this).removeClass('state-hover'); } ); // hex inputs $('input.hex') .validHex() .keyup(function() { $(this).validHex(); }) .click(function(){ $(this).addClass('focus'); $('#picker').remove(); $('div.picker-on').removeClass('picker-on'); $('div.texturePicker ul:visible').hide(0).parent().css('position', 'static'); $(this).after('
').parent().addClass('picker-on'); $('#picker').farbtastic(this); return false; }) .wrap('
') .applyFarbtastic(); // focus and blur classes in form $('input, select') .focus(function() { $('input.focus, select.focus').removeClass('focus'); $(this).addClass('focus'); }) .blur(function() { $(this).removeClass('focus'); }); // texture pickers from select menus $('select.texture').each(function() { $(this).after('
'); var texturePicker = $(this).next(); var a = texturePicker.find('a'); var ul = texturePicker.find('ul'); var sIndex = texturePicker.prev().get(0).selectedIndex; // scrape options $(this).find('option').each(function(){ ul.append('
  • '+ $(this).text() +'
  • '); if($(this).get(0).index == sIndex){texturePicker.attr('title',$(this).text()).css('background', '#555555 url(/themeroller/images/?new=555555&w='+$(this).attr('data-texturewidth')+'&h='+$(this).attr('data-textureheight')+'&f=png&q=60&fltr[]=over|textures/'+$(this).attr('value')+'|0|0|100) 50% 50% repeat');} }); ul.find('li').click(function() { texturePicker.prev().get(0).selectedIndex = texturePicker.prev().find('option[value='+ $(this).attr('class').replace(/\./g, '\\.') +']').get(0).index; texturePicker.attr('title',$(this).text()).css('background', '#555555 url(/themeroller/images/?new=555555&w='+$(this).attr('data-texturewidth')+'&h='+$(this).attr('data-textureheight')+'&f=png&q=60&fltr[]=over|textures/'+$(this).attr('class')+'|0|0|100) 50% 50% repeat'); //ul.fadeOut(100); formChange(); return false; }); // hide the menu and select el ul.hide(); // show/hide of menus texturePicker.click(function() { $(this).addClass('focus'); $('#picker').remove(); var showIt; if(ul.is(':hidden')){showIt = true;} $('div.texturePicker ul:visible').hide().parent().css('position', 'static'); if(showIt == true){ texturePicker.css('position', 'relative'); ul.show(); } return false; }); }); // spindowns in TR panel $('div.theme-group .theme-group-header').addClass('corner-all').spinDown(); // change event in form $('#themeRoller form').bind('change', function() { formChange(); return false; }); // return key triggers form change $.hotkeys.add('return', function () { formChange(); }); //DL theme button $('#downloadTheme').click(function(){ var href = $('link[href*=parseTheme\\.css\\.php]:last').attr('href'); href = href.replace('',''); var themeParams = escape('?' + href.split('?')[1]); var themeParamName = (bookmarklet) ? 'theme' : 'themeParams'; var straightToDownload = (bookmarklet) ? '&ui-version=1.7&download=true' : ''; location.href = '/download/?' + themeParamName + '=' + themeParams + straightToDownload; return false; }); }; // events within the theme gallery function themeGalleryBehaviors() { // loading and viewing gallery themes $('#themeGallery a') .bind('click', function() { updateCSS(hash.clean(this.href.split('?')[1])); hash.updateHash(hash.clean(this.href.split('?')[1]), true); return false; }) .attr('title', 'Click to preview this theme') .each(function(){ var straightToDownload = (bookmarklet) ? '&ui-version=1.7&download=true' : ''; $(this).after( 'Download'+ 'Edit'); }) .parent() .find('a.edit') .click(function(){ $(this).prev().prev().trigger('click'); $('#rollerTabs').tabs( "select", 0 ); return false; }); }; // dom ready event $(function() { //app tabs $('#rollerTabs').tabs({ load: function(e, ui){ rollYourOwnBehaviors(); if(openGroups.length > 0){ openGroups.join(','); $('.theme-group-content:eq('+openGroups+')').prev().trigger('click'); } if(focusedEl){ $('form input, form select, form .texturePicker').eq(focusedEl).click(); } openGroups = []; focusedEl = null; }, spinner: 'Loading...', select: function(e,ui){ if($(ui.panel).is('#rollYourOwn') && hash.currHash() != hash.currentTabHash){ // Stop if we actually don't have a hash change hash.refreshToHash(); } hash.currentTabHash = hash.currHash(); } }); //events and behaviors for rollyourown rollYourOwnBehaviors(); //events and behaviors for themeGallery themeGalleryBehaviors(); //general app click cleanup $('body').click(function() { $('div.picker-on').removeClass('picker-on'); $('#picker').remove(); $('input.focus, select.focus').removeClass('focus'); $('div.texturePicker ul:visible').hide().parent().css('position', 'static'); }); //links to roll your own from help tab $('#help a[href="#rollYourOwn"]').click(function(){ $('#rollerTabs').tabs( "select", 0 ); return false; }); //links to theme gallery from help tab $('#help a[href="#themeGallery"]').click(function(){ $('#rollerTabs').tabs( "select", 1 ); return false; }); //start hash tracking listening hash.init(); }); /* plugin resources from here down */ /****************************************************************************************************************************** * @ Original idea by by Binny V A, Original version: 2.00.A * @ http://www.openjs.com/scripts/events/keyboard_shortcuts/ * @ Original License : BSD * @ jQuery Plugin by Tzury Bar Yochay mail: tzury.by@gmail.com blog: evalinux.wordpress.com face: facebook.com/profile.php?id=513676303 (c) Copyrights 2007 * @ jQuery Plugin version Beta (0.0.2) * @ License: jQuery-License. TODO: add queue support (as in gmail) e.g. 'x' then 'y', etc. add mouse + mouse wheel events. USAGE: $.hotkeys.add('Ctrl+c', function(){ alert('copy anyone?');}); $.hotkeys.add('Ctrl+c', {target:'div#editor', type:'keyup', propagate: true},function(){ alert('copy anyone?');});> $.hotkeys.remove('Ctrl+c'); $.hotkeys.remove('Ctrl+c', {target:'div#editor', type:'keypress'}); ******************************************************************************************************************************/ (function (jQuery){ this.version = '(beta)(0.0.3)'; this.all = {}; this.special_keys = { 27: 'esc', 9: 'tab', 32:'space', 13: 'return', 8:'backspace', 145: 'scroll', 20: 'capslock', 144: 'numlock', 19:'pause', 45:'insert', 36:'home', 46:'del',35:'end', 33: 'pageup', 34:'pagedown', 37:'left', 38:'up', 39:'right',40:'down', 112:'f1',113:'f2', 114:'f3', 115:'f4', 116:'f5', 117:'f6', 118:'f7', 119:'f8', 120:'f9', 121:'f10', 122:'f11', 123:'f12'}; this.shift_nums = { "`":"~", "1":"!", "2":"@", "3":"#", "4":"$", "5":"%", "6":"^", "7":"&", "8":"*", "9":"(", "0":")", "-":"_", "=":"+", ";":":", "'":"\"", ",":"<", ".":">", "/":"?", "\\":"|" }; this.add = function(combi, options, callback) { if (jQuery.isFunction(options)){ callback = options; options = {}; } var opt = {}, defaults = {type: 'keydown', propagate: false, disableInInput: false, target: jQuery('html')[0], checkParent: true}, that = this; opt = jQuery.extend( opt , defaults, options || {} ); combi = combi.toLowerCase(); // inspect if keystroke matches var inspector = function(event) { event = jQuery.event.fix(event); // jQuery event normalization. var element = event.target; // @ TextNode -> nodeType == 3 element = (element.nodeType==3) ? element.parentNode : element; if(opt['disableInInput']) { // Disable shortcut keys in Input, Textarea fields var target = jQuery(element); if( target.is("input") || target.is("textarea")){ return; } } var code = event.which, type = event.type, character = String.fromCharCode(code).toLowerCase(), special = that.special_keys[code], shift = event.shiftKey, ctrl = event.ctrlKey, alt= event.altKey, propagate = true, // default behaivour mapPoint = null; // in opera + safari, the event.target is unpredictable. // for example: 'keydown' might be associated with HtmlBodyElement // or the element where you last clicked with your mouse. if (jQuery.browser.opera || jQuery.browser.safari || opt.checkParent){ while (!that.all[element] && element.parentNode){ element = element.parentNode; } } var cbMap = that.all[element].events[type].callbackMap; if(!shift && !ctrl && !alt) { // No Modifiers mapPoint = cbMap[special] || cbMap[character]; } // deals with combinaitons (alt|ctrl|shift+anything) else{ var modif = ''; if(alt) modif +='alt+'; if(ctrl) modif+= 'ctrl+'; if(shift) modif += 'shift+'; // modifiers + special keys or modifiers + characters or modifiers + shift characters mapPoint = cbMap[modif+special] || cbMap[modif+character] || cbMap[modif+that.shift_nums[character]]; } if (mapPoint){ mapPoint.cb(event); if(!mapPoint.propagate) { event.stopPropagation(); event.preventDefault(); return false; } } }; // first hook for this element if (!this.all[opt.target]){ this.all[opt.target] = {events:{}}; } if (!this.all[opt.target].events[opt.type]){ this.all[opt.target].events[opt.type] = {callbackMap: {}}; jQuery.event.add(opt.target, opt.type, inspector); } this.all[opt.target].events[opt.type].callbackMap[combi] = {cb: callback, propagate:opt.propagate}; return jQuery; }; this.remove = function(exp, opt) { opt = opt || {}; target = opt.target || jQuery('html')[0]; type = opt.type || 'keydown'; exp = exp.toLowerCase(); delete this.all[target].events[type].callbackMap[exp]; return jQuery; }; jQuery.hotkeys = this; return jQuery; })(jQuery); // $Id: farbtastic.js,v 1.2 2007/01/08 22:53:01 unconed Exp $ // Farbtastic 1.2 jQuery.fn.farbtastic = function (callback) { $.farbtastic(this, callback); return this; }; jQuery.farbtastic = function (container, callback) { var container = $(container).get(0); return container.farbtastic || (container.farbtastic = new jQuery._farbtastic(container, callback)); }; jQuery._farbtastic = function (container, callback) { // Store farbtastic object var fb = this; // Insert markup $(container).html('
    '); var e = $('.farbtastic', container); fb.wheel = $('.wheel', container).get(0); // Dimensions fb.radius = 84; fb.square = 100; fb.width = 194; // Fix background PNGs in IE6 if (navigator.appVersion.match(/MSIE [0-6]\./)) { $('*', e).each(function () { if (this.currentStyle.backgroundImage != 'none') { var image = this.currentStyle.backgroundImage; image = this.currentStyle.backgroundImage.substring(5, image.length - 2); $(this).css({ 'backgroundImage': 'none', 'filter': "progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='" + image + "')" }); } }); } /** * Link to the given element(s) or callback. */ fb.linkTo = function (callback) { // Unbind previous nodes if (typeof fb.callback == 'object') { $(fb.callback).unbind('keyup', fb.updateValue); } // Reset color fb.color = null; // Bind callback or elements if (typeof callback == 'function') { fb.callback = callback; } else if (typeof callback == 'object' || typeof callback == 'string') { fb.callback = $(callback); fb.callback.bind('keyup', fb.updateValue); if (fb.callback.get(0).value) { fb.setColor(fb.callback.get(0).value); } } return this; }; fb.updateValue = function (event) { if (this.value && this.value != fb.color) { fb.setColor(this.value); } }; /** * Change color with HTML syntax #123456 */ fb.setColor = function (color) { var unpack = fb.unpack(color); if (fb.color != color && unpack) { fb.color = color; fb.rgb = unpack; fb.hsl = fb.RGBToHSL(fb.rgb); fb.updateDisplay(); } return this; }; /** * Change color with HSL triplet [0..1, 0..1, 0..1] */ fb.setHSL = function (hsl) { fb.hsl = hsl; fb.rgb = fb.HSLToRGB(hsl); fb.color = fb.pack(fb.rgb); fb.updateDisplay(); return this; }; ///////////////////////////////////////////////////// /** * Retrieve the coordinates of the given event relative to the center * of the widget. */ fb.widgetCoords = function (event) { var x, y; var el = event.target || event.srcElement; var reference = fb.wheel; if (typeof event.offsetX != 'undefined') { // Use offset coordinates and find common offsetParent var pos = { x: event.offsetX, y: event.offsetY }; // Send the coordinates upwards through the offsetParent chain. var e = el; while (e) { e.mouseX = pos.x; e.mouseY = pos.y; pos.x += e.offsetLeft; pos.y += e.offsetTop; e = e.offsetParent; }; // Look for the coordinates starting from the wheel widget. var e = reference; var offset = { x: 0, y: 0 }; while (e) { if (typeof e.mouseX != 'undefined') { x = e.mouseX - offset.x; y = e.mouseY - offset.y; break; } offset.x += e.offsetLeft; offset.y += e.offsetTop; e = e.offsetParent; } // Reset stored coordinates e = el; while (e) { e.mouseX = undefined; e.mouseY = undefined; e = e.offsetParent; } } else { // Use absolute coordinates var pos = fb.absolutePosition(reference); x = (event.pageX || 0*(event.clientX + $('html').get(0).scrollLeft)) - pos.x; y = (event.pageY || 0*(event.clientY + $('html').get(0).scrollTop)) - pos.y; } // Subtract distance to middle return { x: x - fb.width / 2, y: y - fb.width / 2 }; }; /** * Mousedown handler */ fb.mousedown = function (event) { // Capture mouse if (!document.dragging) { $(document).bind('mousemove', fb.mousemove).bind('mouseup', fb.mouseup); document.dragging = true; }; // Check which area is being dragged var pos = fb.widgetCoords(event); fb.circleDrag = Math.max(Math.abs(pos.x), Math.abs(pos.y)) * 2 > fb.square; // Process fb.mousemove(event); return false; }; /** * Mousemove handler */ fb.mousemove = function (event) { // Get coordinates relative to color picker center var pos = fb.widgetCoords(event); // Set new HSL parameters if (fb.circleDrag) { var hue = Math.atan2(pos.x, -pos.y) / 6.28; if (hue < 0) hue += 1; fb.setHSL([hue, fb.hsl[1], fb.hsl[2]]); } else { var sat = Math.max(0, Math.min(1, -(pos.x / fb.square) + .5)); var lum = Math.max(0, Math.min(1, -(pos.y / fb.square) + .5)); fb.setHSL([fb.hsl[0], sat, lum]); } return false; }; /** * Mouseup handler */ fb.mouseup = function () { // Uncapture mouse $(document).unbind('mousemove', fb.mousemove); $(document).unbind('mouseup', fb.mouseup); document.dragging = false; formChange(); }; /** * Update the markers and styles */ fb.updateDisplay = function () { // Markers var angle = fb.hsl[0] * 6.28; $('.h-marker', e).css({ left: Math.round(Math.sin(angle) * fb.radius + fb.width / 2) + 'px', top: Math.round(-Math.cos(angle) * fb.radius + fb.width / 2) + 'px' }); $('.sl-marker', e).css({ left: Math.round(fb.square * (.5 - fb.hsl[1]) + fb.width / 2) + 'px', top: Math.round(fb.square * (.5 - fb.hsl[2]) + fb.width / 2) + 'px' }); // Saturation/Luminance gradient $('.color', e).css('backgroundColor', fb.pack(fb.HSLToRGB([fb.hsl[0], 1, 0.5]))); // Linked elements or callback if (typeof fb.callback == 'object') { // Set background/foreground color $(fb.callback).css({ backgroundColor: fb.color, color: fb.hsl[2] > 0.5 ? '#000' : '#fff' }); // Change linked value $(fb.callback).each(function() { if (this.value && this.value != fb.color) { this.value = fb.color; } }); } else if (typeof fb.callback == 'function') { fb.callback.call(fb, fb.color); } }; /** * Get absolute position of element */ fb.absolutePosition = function (el) { var r = { x: el.offsetLeft, y: el.offsetTop }; // Resolve relative to offsetParent if (el.offsetParent) { var tmp = fb.absolutePosition(el.offsetParent); r.x += tmp.x; r.y += tmp.y; } return r; }; /* Various color utility functions */ fb.pack = function (rgb) { var r = Math.round(rgb[0] * 255); var g = Math.round(rgb[1] * 255); var b = Math.round(rgb[2] * 255); return '#' + (r < 16 ? '0' : '') + r.toString(16) + (g < 16 ? '0' : '') + g.toString(16) + (b < 16 ? '0' : '') + b.toString(16); }; fb.unpack = function (color) { if (color.length == 7) { return [parseInt('0x' + color.substring(1, 3)) / 255, parseInt('0x' + color.substring(3, 5)) / 255, parseInt('0x' + color.substring(5, 7)) / 255]; } else if (color.length == 4) { return [parseInt('0x' + color.substring(1, 2)) / 15, parseInt('0x' + color.substring(2, 3)) / 15, parseInt('0x' + color.substring(3, 4)) / 15]; } }; fb.HSLToRGB = function (hsl) { var m1, m2, r, g, b; var h = hsl[0], s = hsl[1], l = hsl[2]; m2 = (l <= 0.5) ? l * (s + 1) : l + s - l*s; m1 = l * 2 - m2; return [this.hueToRGB(m1, m2, h+0.33333), this.hueToRGB(m1, m2, h), this.hueToRGB(m1, m2, h-0.33333)]; }; fb.hueToRGB = function (m1, m2, h) { h = (h < 0) ? h + 1 : ((h > 1) ? h - 1 : h); if (h * 6 < 1) return m1 + (m2 - m1) * h * 6; if (h * 2 < 1) return m2; if (h * 3 < 2) return m1 + (m2 - m1) * (0.66666 - h) * 6; return m1; }; fb.RGBToHSL = function (rgb) { var min, max, delta, h, s, l; var r = rgb[0], g = rgb[1], b = rgb[2]; min = Math.min(r, Math.min(g, b)); max = Math.max(r, Math.max(g, b)); delta = max - min; l = (min + max) / 2; s = 0; if (l > 0 && l < 1) { s = delta / (l < 0.5 ? (2 * l) : (2 - 2 * l)); } h = 0; if (delta > 0) { if (max == r && max != g) h += (g - b) / delta; if (max == g && max != b) h += (2 + (b - r) / delta); if (max == b && max != r) h += (4 + (r - g) / delta); h /= 6; } return [h, s, l]; }; // Install mousedown handler (the others are set on the document on-demand) $('*', e).mousedown(fb.mousedown); // Init color fb.setColor('#000000'); // Set linked elements/callback if (callback) { fb.linkTo(callback); } };