Updating the agenda view for event plugin.
authorSteve Sutton <steve@gaslightmedia.com>
Wed, 10 Oct 2018 14:43:32 +0000 (10:43 -0400)
committerSteve Sutton <steve@gaslightmedia.com>
Wed, 10 Oct 2018 14:43:32 +0000 (10:43 -0400)
Update the agenda view to use the one created for visitalpena.
Adding new management options for setting the colors for the agenda
view. This allows the colors to be set without updating the theme.
The old agenda view is saved as agenda_old.html so it can be setup as
the template attribute in the shortcode to keep that template.

13 files changed:
classes/data/dataManagement.php
css/wheelcolorpicker.css [new file with mode: 0644]
index.php
js/jquery.wheelcolorpicker.js [new file with mode: 0644]
models/admin/management/events.php
setup/databaseScripts/create_database_V0.1.7.sql [deleted file]
setup/databaseScripts/create_database_V0.1.8.sql [new file with mode: 0644]
setup/databaseScripts/dbVersions.php
setup/databaseScripts/steve-V0.1.8.sql [deleted file]
setup/databaseScripts/update_database_V0.1.8.sql [new file with mode: 0644]
views/admin/management/events.html
views/front/events/agenda.html
views/front/events/agenda_old.html [new file with mode: 0644]

index bf2e23a..ad2f7b2 100644 (file)
@@ -247,6 +247,70 @@ class GlmDataEventsManagement extends GlmDataAbstract
                 'use'      => 'a',
             ),
 
+            // Color of the search button
+            'event_add_button_color' => array(
+                'field'    => 'event_add_button_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Option to hide the add event button
+            'event_add_button_hidden' => array(
+                'field'   => 'event_add_button_hidden',
+                'type'    => 'checkbox',
+                'default' => false,
+                'use'     => 'a'
+            ),
+
+            // Color of the search button
+            'event_back_to_search_color' => array(
+                'field'    => 'event_back_to_search_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Color of the search button
+            'agenda_date_background_color' => array(
+                'field'    => 'agenda_date_background_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Color of the search button
+            'agenda_date_text_color' => array(
+                'field'    => 'agenda_date_text_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Color of the search button
+            'agenda_title_color' => array(
+                'field'    => 'agenda_title_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Color of the search button
+            'agenda_container_background_color' => array(
+                'field'    => 'agenda_container_background_color',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
+            // Max Width of Agenda View
+            'agenda_view_max_width' => array(
+                'field'    => 'agenda_view_max_width',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a',
+            ),
+
          );
 
     }
diff --git a/css/wheelcolorpicker.css b/css/wheelcolorpicker.css
new file mode 100644 (file)
index 0000000..1272619
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ * jQuery Wheel Color Picker
+ * Base Stylesheet
+ * 
+ * http://www.jar2.net/projects/jquery-wheelcolorpicker
+ * 
+ * Copyright © 2011-2016 Fajar Chandra. All rights reserved.
+ * Released under MIT License.
+ * http://www.opensource.org/licenses/mit-license.php
+ * 
+ * Note: Width, height, left, and top properties are handled by the 
+ * plugin. These values might change on the fly.
+ */
+
+.jQWCP-wWidget {
+       position: absolute;
+       width: 250px;
+       height: 180px;
+       background: #eee;
+       box-shadow: 1px 1px 4px rgba(0,0,0,.5);
+       border-radius: 4px;
+       border: solid 1px #aaa;
+       padding: 10px;
+       z-index: 1001;
+}
+
+.jQWCP-wWidget.jQWCP-block {
+       position: relative;
+       border-color: #aaa;
+       box-shadow: inset 1px 1px 1px #ccc;
+}
+
+.jQWCP-wWheel {
+       background-repeat: no-repeat;
+       background-position: center;
+       background-size: contain;
+       position: relative;
+       float: left;
+       width: 180px;
+       height: 180px;
+       -webkit-border-radius: 90px;
+       -moz-border-radius: 50%;
+       border-radius: 50%;
+       border: solid 1px #aaa;
+       margin: -1px;
+       margin-right: 10px;
+       transition: border .15s;
+       cursor: crosshair;
+}
+
+.jQWCP-wWheel:hover {
+       border-color: #666;
+}
+
+.jQWCP-wWheelOverlay {
+       position: absolute;
+       top: 0;
+       left: 0;
+       width: 100%;
+       height: 100%;
+       background: #000;
+       opacity: 0;
+       -webkit-border-radius: 90px;
+       -moz-border-radius: 50%;
+       border-radius: 50%;
+}
+
+.jQWCP-wWheelCursor {
+       width: 8px;
+       height: 8px;
+       position: absolute;
+       top: 50%;
+       left: 50%;
+       margin: -6px -6px;
+       cursor: crosshair;
+       border: solid 2px #fff;
+       box-shadow: 1px 1px 2px #000;
+       border-radius: 50%;
+}
+
+.jQWCP-slider-wrapper,
+.jQWCP-wPreview {
+       position: relative;
+       width: 20px;
+       height: 180px;
+       float: left;
+       margin-right: 10px;
+}
+
+.jQWCP-wWheel:last-child,
+.jQWCP-slider-wrapper:last-child,
+.jQWCP-wPreview:last-child {
+    margin-right: 0;
+}
+
+.jQWCP-slider,
+.jQWCP-wPreviewBox {
+       position: absolute;
+       width: 100%;
+       height: 100%;
+       left: 0;
+       top: 0;
+       box-sizing: border-box;
+       border: solid 1px #aaa;
+       margin: -1px;
+       -moz-border-radius: 4px;
+       border-radius: 4px;
+       transition: border .15s;
+}
+
+.jQWCP-slider {
+       cursor: crosshair;
+}
+
+.jQWCP-slider-wrapper:hover .jQWCP-slider {
+       border-color: #666;
+}
+
+.jQWCP-scursor {
+       position: absolute;
+       left: 0;
+       top: 0;
+       right: 0;
+       height: 6px;
+       margin: -5px -1px -5px -3px;
+       cursor: crosshair;
+       border: solid 2px #fff;
+       box-shadow: 1px 1px 2px #000;
+       border-radius: 4px;
+}
+
+.jQWCP-wAlphaSlider,
+.jQWCP-wPreviewBox {
+       background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEVAQEB/f39eaJUuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QYRBDgK9dKdMgAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAARSURBVAjXY/jPwIAVYRf9DwB+vw/x6vMT1wAAAABJRU5ErkJggg==') center center;
+}
+
+.jQWCP-overlay {
+       position: fixed;
+       top: 0;
+       left: 0;
+       bottom: 0;
+       right: 0;
+       z-index: 1000;
+}
+
+/*********************/
+
+/* Mobile layout */
+
+.jQWCP-mobile.jQWCP-wWidget {
+    position: fixed;
+    bottom: 0;
+    left: 0 !important;
+    top: auto !important;
+    width: 100%;
+    height: 75%;
+    max-height: 240px;
+    box-sizing: border-box;
+    border-radius: 0;
+}
index f069dd5..4ddb209 100644 (file)
--- a/index.php
+++ b/index.php
@@ -44,7 +44,7 @@ if (!defined('ABSPATH')) {
  *  version from this plugin.
  */
 define('GLM_MEMBERS_EVENTS_PLUGIN_VERSION', '1.7.0');
-define('GLM_MEMBERS_EVENTS_PLUGIN_DB_VERSION', '0.1.7');
+define('GLM_MEMBERS_EVENTS_PLUGIN_DB_VERSION', '0.1.8');
 
 // This is the minimum version of the GLM Members DB plugin require for this plugin.
 define('GLM_MEMBERS_EVENTS_PLUGIN_MIN_MEMBERS_REQUIRED_VERSION', '2.9.15');
diff --git a/js/jquery.wheelcolorpicker.js b/js/jquery.wheelcolorpicker.js
new file mode 100644 (file)
index 0000000..9cdeca8
--- /dev/null
@@ -0,0 +1,2625 @@
+/**
+ * jQuery Wheel Color Picker
+ * 
+ * https://raffer.one/projects/jquery-wheelcolorpicker
+ * 
+ * Author : Fajar Chandra
+ * Date   : 2018.02.09
+ * 
+ * Copyright © 2011-2018 Fajar Chandra. All rights reserved.
+ * Released under MIT License.
+ * http://www.opensource.org/licenses/mit-license.php
+ */
+
+(function ($) {
+       
+       /**
+        * Function: wheelColorPicker
+        * 
+        * The wheelColorPicker plugin wrapper. Firing all functions and 
+        * setting/getting all options in this plugin should be called via 
+        * this function.
+        * 
+        * Before that, if wheelColorPicker instance is not yet initialized, 
+        * this will initialize ColorPicker widget.
+        * 
+        * This function will look for the options parameter passed in, and 
+        * try to do something as specified in this order:
+        * 1. If no argument is passed, then initialize the plugin or do nothing
+        * 2. If object is passed, then call setOptions()
+        * 3. If string is passed, then try to fire a method with that name
+        * 4. If string is passed and no method matches the name, then try 
+        *    to set/get an option with that name. If a setter/getter method 
+        *    available (i.e. setSomething), it will set/get that option via that method.
+        */
+       $.fn.wheelColorPicker = function() {
+               var returnValue = this; // Allows method chaining
+               
+               // Separate first argument and the rest..
+               // First argument is used to determine function/option name
+               // e.g. wheelColorPicker('setColor', { r: 0, g: 0, b: 1 })
+               if(arguments.length > 0) {
+                       var shift = [].shift;
+                       var firstArg = shift.apply(arguments);
+                       var firstArgUc = (typeof firstArg === "string") ? firstArg.charAt(0).toUpperCase() + firstArg.slice(1) : firstArg;
+               } 
+               else {
+                       var firstArg = undefined;
+                       var firstArgUc = undefined;
+               }
+               var args = arguments;
+               
+               
+               this.each(function() {
+                       
+                       // Grab ColorPicker object instance
+                       var instance = $(this).data('jQWCP.instance');
+                       
+                       // Initialize if not yet created
+                       if(instance == undefined || instance == null) {
+                               // Get init options
+                               var options = {};
+                               if(typeof firstArg === "object") {
+                                       options = firstArg;
+                               }
+                               
+                               instance = new WCP.ColorPicker(this, options);
+                               $(this).data('jQWCP.instance', instance);
+                       }
+                       
+                       /// What to do? ///
+                       
+                       // No arguments provided, do nothing
+                       // wheelColorPicker()
+                       if(firstArg === undefined || typeof firstArg === "object") {
+                       }
+                       
+                       // Call a method
+                       // wheelColorPicker('show')
+                       else if(typeof instance[firstArg] === "function") {
+                               //console.log('method');
+                               var ret = instance[firstArg].apply(instance, args);
+                               
+                               // If instance is not returned, no method chaining
+                               if(ret !== instance) {
+                                       returnValue = ret;
+                                       return false;
+                               }
+                       }
+                       
+                       // Try option setter
+                       // wheelColorPicker('color', '#ff00aa')
+                       else if(typeof instance['set'+firstArgUc] === "function" && args.length > 0) {
+                               //console.log('setter');
+                               var ret = instance['set'+firstArgUc].apply(instance, args);
+                               
+                               // If instance is not returned, no method chaining
+                               if(ret !== instance) {
+                                       returnValue = ret;
+                                       return false;
+                               }
+                       }
+                       
+                       // Try option getter
+                       // wheelColorPicker('color')
+                       else if(typeof instance['get'+firstArgUc] === "function") {
+                               //console.log('getter');
+                               var ret = instance['get'+firstArgUc].apply(instance, args);
+                               
+                               // If instance is not returned, no method chaining
+                               if(ret !== instance) {
+                                       returnValue = ret;
+                                       return false;
+                               }
+                       }
+                       
+                       // Set option value
+                       // wheelColorPicker('format', 'hex')
+                       else if(instance.options[firstArg] !== undefined && args.length > 0) {
+                               //console.log('set option');
+                               instance.options[firstArg] = args[0];
+                       }
+                       
+                       // Get option value
+                       // wheelColorPicker('format')
+                       else if(instance.options[firstArg] !== undefined) {
+                               //console.log('get option');
+                               returnValue = instance.options[firstArg];
+                               return false;
+                       }
+                       
+                       // Nothing matches, throw error
+                       else {
+                               $.error( 'Method/option named ' +  firstArg + ' does not exist on jQuery.wheelColorPicker' );
+                       }
+                       
+               });
+               
+               return returnValue;
+       };
+
+
+
+       /******************************************************************/
+
+       /////////////////////////////////////////
+       // Shorthand for $.fn.wheelColorPicker //
+       /////////////////////////////////////////
+       var WCP = $.fn.wheelColorPicker;
+       /////////////////////////////////////////
+
+       /**
+        * Object: defaults
+        * 
+        * Contains default options for the wheelColorPicker plugin.
+        * 
+        * Member properties:
+        * 
+        *   format        - <string> Color naming style. See colorToRgb for all 
+        *                   available formats.
+        *   live          - <boolean> Enable dynamic slider gradients.
+        *   preview       - <boolean> Enable live color preview on input field
+        *   userinput     - (Deprecated) <boolean> Enable picking color by typing directly
+        *   validate      - <boolean> When userinput is enabled, force the value to be 
+        *                   a valid format. If user input an invalid color, the value 
+        *                   will be reverted to last valid color.
+     *   autoResize    - <boolean> Automatically resize container width.
+     *                   If set to false, you could manually adjust size with CSS.
+        *   autoFormat    - <boolean> Automatically convert input value to 
+        *                   specified format. For example, if format is "rgb", 
+        *                   "#ff0000" will automatically converted into "rgb(255,0,0)".
+        *   color         - <object|string> Initial value in any of supported color 
+        *                   value format or as an object. Setting this value will 
+        *                   override the current input value.
+        *   alpha         - (Deprecated) <boolean> Force the color picker to use alpha value 
+        *                   despite its selected color format. This option is 
+        *                   deprecated. Use sliders = "a" instead.
+        *   inverseLabel  - (deprecated) Boolean use inverse color for 
+        *                   input label instead of black/white color.
+        *   preserveWheel - Boolean preserve color wheel shade when slider 
+        *                   position changes. If set to true, changing 
+        *                   color wheel from black will reset selectedColor.val 
+        *                   (shade) to 1.
+        *   interactive   - Boolean enable interactive sliders where slider bar
+        *                   gradients change dynamically as user drag a slider 
+        *                   handle. Set to false if this affect performance.
+        *                   See also 'quality' option if you wish to keep 
+        *                   interactive slider but with reduced quality.
+        *   cssClass      - Object CSS Classes to be added to the color picker.
+        *   layout        - String [block|popup] Layout mode.
+        *   animDuration  - Number Duration for transitions such as fade-in 
+        *                   and fade-out.
+        *   quality       - Rendering details quality. The normal quality is 1. 
+        *                   Setting less than 0.1 may make the sliders ugly, 
+        *                   while setting the value too high might affect performance.
+        *   sliders       - String combination of sliders. If null then the color 
+        *                   picker will show default values, which is "wvp" for 
+        *                   normal color or "wvap" for color with alpha value. 
+        *                   Possible combinations are "whsvrgbap". 
+     *                   Order of letters will affect slider positions.
+        *   sliderLabel   - Boolean Show labels for each slider.
+        *   sliderValue   - Boolean Show numeric value of each slider.
+        *   hideKeyboard  - Boolean Keep input blurred to avoid on screen keyboard appearing. 
+        *                   If this is set to true, avoid assigning handler to blur event.
+        *   rounding      - Round the alpha value to N decimal digits. Default is 2.
+        *                   Set -1 to disable rounding.
+        *   mobile        - Display mobile-friendly layout when opened in mobile device.
+     *   mobileWidth   - Max screen width to use mobile layout instead of default one.
+        *   mobileAutoScroll - Automatically scroll the page if focused input element 
+        *                      gets obstructed by color picker dialog.
+        *   htmlOptions   - Load options from HTML attributes. 
+        *                   To set options using HTML attributes, 
+        *                   prefix these options with 'data-wcp-' as attribute names.
+     *   snap          - Snap color wheel and slider on 0, 0.5, and 1.0
+     *   snapTolerance - Snap if slider position falls within defined 
+     *                   tolerance value.
+        */
+       WCP.defaults = {
+               format: 'hex', /* 1.x */
+               preview: false, /* 1.x */
+               live: true, /* 2.0 */
+               userinput: true, /* DEPRECATED 1.x */
+               validate: true, /* 1.x */
+               autoResize: true, /* 3.0 */
+               autoFormat: true, /* 3.0 */
+               //color: null, /* DEPRECATED 1.x */ /* OBSOLETE 3.0 */ /* Init-time only */
+               //alpha: null, /* DEPRECATED 1.x */ /* OBSOLETE 3.0 */ /* See methods.alpha */
+               preserveWheel: null, /* DEPRECATED 1.x */ /* Use live */
+               cssClass: '', /* 2.0 */
+               layout: 'popup', /* 2.0 */ /* Init-time only */
+               animDuration: 200, /* 2.0 */
+               quality: 1, /* 2.0 */
+               sliders: null, /* 2.0 */
+               //sliderLabel: true, /* 2.0 */ /* NOT IMPLEMENTED */
+               //sliderValue: false, /* 2.0 */ /* NOT IMPLEMENTED */
+               rounding: 2, /* 2.3 */
+               mobile: true, /* 3.0 */
+        mobileWidth: 480, /* 3.0 */
+               hideKeyboard: false, /* 2.4 */
+               htmlOptions: true, /* 2.3 */
+        snap: false, /* 2.5 */
+               snapTolerance: 0.05 /* 2.5 */
+       };
+
+
+
+       /******************************************************************/
+
+       //////////////////////////////
+       // STATIC OBJECTS AND FLAGS //
+       //////////////////////////////
+
+       /* 
+        * Note: To determine input position (top and left), use the following:
+        * WCP.ORIGIN.top + this.input.getBoundingClientRect().top
+        * instead of using $(this.input).offset().top because on mobile browsers 
+        * (chrome) jQuery's offset() function returns wrong value.
+        */
+
+       /// Top left of the page is not on (0,0), making window.scrollX/Y and offset() useless
+       /// See WCP.ORIGIN
+       WCP.BUG_RELATIVE_PAGE_ORIGIN = false;
+
+       /// Coordinate of the top left page (mobile chrome workaround)
+       WCP.ORIGIN = { left: 0, top: 0 };
+       
+       
+       /******************************************************************/
+
+       //////////////////////
+       // HELPER FUNCTIONS //
+       //////////////////////
+
+       /**
+        * Function: colorToStr
+        * 
+        * Since 2.0
+        * 
+        * Convert color object to string in specified format
+        * 
+        * Available formats:
+        * - hex
+        * - hexa
+        * - css
+        * - cssa
+        * - rgb
+        * - rgb%
+        * - rgba
+        * - rgba%
+        * - hsv
+        * - hsv%
+        * - hsva
+        * - hsva%
+        * - hsb
+        * - hsb%
+        * - hsba
+        * - hsba%
+        */
+       WCP.colorToStr = function( color, format ) {
+               var result = "";
+               switch( format ) {
+                       case 'css':
+                               result = "#";
+                       case 'hex': 
+                               var r = Math.round(color.r * 255).toString(16);
+                               if( r.length == 1) {
+                                       r = "0" + r;
+                               }
+                               var g = Math.round(color.g * 255).toString(16);
+                               if( g.length == 1) {
+                                       g = "0" + g;
+                               }
+                               var b = Math.round(color.b * 255).toString(16);
+                               if( b.length == 1) {
+                                       b = "0" + b;
+                               }
+                               result += r + g + b;
+                               break;
+                               
+                       case 'cssa':
+                               result = "#";
+                       case 'hexa': 
+                               var r = Math.round(color.r * 255).toString(16);
+                               if( r.length == 1) {
+                                       r = "0" + r;
+                               }
+                               var g = Math.round(color.g * 255).toString(16);
+                               if( g.length == 1) {
+                                       g = "0" + g;
+                               }
+                               var b = Math.round(color.b * 255).toString(16);
+                               if( b.length == 1) {
+                                       b = "0" + b;
+                               }
+                               var a = Math.round(color.a * 255).toString(16);
+                               if( a.length == 1) {
+                                       a = "0" + a;
+                               }
+                               result += r + g + b + a;
+                               break;
+                               
+                       case 'rgb':
+                               result = "rgb(" + 
+                                       Math.round(color.r * 255) + "," + 
+                                       Math.round(color.g * 255) + "," + 
+                                       Math.round(color.b * 255) + ")";
+                               break;
+                               
+                       case 'rgb%':
+                               result = "rgb(" + 
+                                       (color.r * 100) + "%," + 
+                                       (color.g * 100) + "%," + 
+                                       (color.b * 100) + "%)";
+                               break;
+                               
+                       case 'rgba':
+                               result = "rgba(" + 
+                                       Math.round(color.r * 255) + "," + 
+                                       Math.round(color.g * 255) + "," + 
+                                       Math.round(color.b * 255) + "," + 
+                                       color.a + ")";
+                               break;
+                               
+                       case 'rgba%':
+                               result = "rgba(" + 
+                                       (color.r * 100) + "%," + 
+                                       (color.g * 100) + "%," + 
+                                       (color.b * 100) + "%," + 
+                                       (color.a * 100) + "%)";
+                               break;
+                               
+                       case 'hsv':
+                               result = "hsv(" + 
+                                       (color.h * 360) + "," + 
+                                       color.s + "," + 
+                                       color.v + ")";
+                               break;
+                               
+                       case 'hsv%':
+                               result = "hsv(" + 
+                                       (color.h * 100) + "%," + 
+                                       (color.s * 100) + "%," + 
+                                       (color.v * 100) + "%)";
+                               break;
+                               
+                       case 'hsva':
+                               result = "hsva(" + 
+                                       (color.h * 360) + "," + 
+                                       color.s + "," + 
+                                       color.v + "," + 
+                                       color.a + ")";
+                               break;
+                               
+                       case 'hsva%':
+                               result = "hsva(" + 
+                                       (color.h * 100) + "%," + 
+                                       (color.s * 100) + "%," + 
+                                       (color.v * 100) + "%," + 
+                                       (color.a * 100) + "%)";
+                               break;
+                               
+                               
+                       case 'hsb':
+                               result = "hsb(" + 
+                                       color.h + "," + 
+                                       color.s + "," + 
+                                       color.v + ")";
+                               break;
+                               
+                       case 'hsb%':
+                               result = "hsb(" + 
+                                       (color.h * 100) + "%," + 
+                                       (color.s * 100) + "%," + 
+                                       (color.v * 100) + "%)";
+                               break;
+                               
+                       case 'hsba':
+                               result = "hsba(" + 
+                                       color.h + "," + 
+                                       color.s + "," + 
+                                       color.v + "," + 
+                                       color.a + ")";
+                               break;
+                               
+                       case 'hsba%':
+                               result = "hsba(" + 
+                                       (color.h * 100) + "%," + 
+                                       (color.s * 100) + "%," + 
+                                       (color.v * 100) + "%," + 
+                                       (color.a * 100) + "%)";
+                               break;
+                               
+               }
+               return result;
+       };
+
+
+       /**
+        * Function: strToColor
+        * 
+        * Since 2.0
+        * 
+        * Convert string to color object.
+        * Please note that if RGB color is supplied, the returned value 
+        * will only contain RGB.
+        * 
+        * If invalid string is supplied, FALSE will be returned.
+        */
+       WCP.strToColor = function( val ) {
+               var color = { a: 1 };
+               var tmp;
+               var hasAlpha;
+               
+               // #fff
+               // #ffff
+               if(
+                       val.match(/^#[0-9a-f]{3}$/i) != null ||
+                       val.match(/^#[0-9a-f]{4}$/i)
+               ) {
+                       if( isNaN( color.r = parseInt(val.substr(1, 1), 16) * 17 / 255 ) ) {
+                               return false;
+                       }
+                       if( isNaN( color.g = parseInt(val.substr(2, 1), 16) * 17 / 255 ) ) {
+                               return false;   
+                       }
+                       if( isNaN( color.b = parseInt(val.substr(3, 1), 16) * 17 / 255 ) ) {
+                               return false;
+                       }
+                       
+                       // Alpha
+                       if(val.length == 5) {
+                               if( isNaN( color.a = parseInt(val.substr(4, 1), 16) * 17 / 255 ) ) {
+                                       return false;
+                               }
+                       }
+               }
+               
+               // fff
+               // ffff
+               else if(
+                       val.match(/^[0-9a-f]{3}$/i) != null ||
+                       val.match(/^[0-9a-f]{4}$/i) != null
+               ) {
+                       if( isNaN( color.r = parseInt(val.substr(0, 1), 16) * 17 / 255 ) ) {
+                               return false;
+                       }
+                       if( isNaN( color.g = parseInt(val.substr(1, 1), 16) * 17 / 255 ) ) {
+                               return false;   
+                       }
+                       if( isNaN( color.b = parseInt(val.substr(2, 1), 16) * 17 / 255 ) ) {
+                               return false;
+                       }
+                       
+                       // Alpha
+                       if(val.length == 4) {
+                               if( isNaN( color.a = parseInt(val.substr(3, 1), 16) * 17 / 255 ) ) {
+                                       return false;
+                               }
+                       }
+               }
+               
+               // #ffffff
+               // #ffffffff
+               else if(
+                       val.match(/^#[0-9a-f]{6}$/i) != null ||
+                       val.match(/^#[0-9a-f]{8}$/i) != null
+               ) {
+                       if( isNaN( color.r = parseInt(val.substr(1, 2), 16) / 255 ) ) {
+                               return false;
+                       }
+                       if( isNaN( color.g = parseInt(val.substr(3, 2), 16) / 255 ) ) {
+                               return false;   
+                       }
+                       if( isNaN( color.b = parseInt(val.substr(5, 2), 16) / 255 ) ) {
+                               return false;
+                       }
+                       
+                       // Alpha
+                       if(val.length == 9) {
+                               if( isNaN( color.a = parseInt(val.substr(7, 2), 16) / 255 ) ) {
+                                       return false;
+                               }
+                       }
+               }
+               
+               // ffffff
+               // ffffffff
+               else if(
+                       val.match(/^[0-9a-f]{6}$/i) != null ||
+                       val.match(/^[0-9a-f]{8}$/i) != null
+               ) {
+                       if( isNaN( color.r = parseInt(val.substr(0, 2), 16) / 255 ) ) {
+                               return false;
+                       }
+                       if( isNaN( color.g = parseInt(val.substr(2, 2), 16) / 255 ) ) {
+                               return false;
+                       }
+                       if( isNaN( color.b = parseInt(val.substr(4, 2), 16) / 255 ) ) {
+                               return false;
+                       }
+                       
+                       // Alpha
+                       if(val.length == 8) {
+                               if( isNaN( color.a = parseInt(val.substr(6, 2), 16) / 255 ) ) {
+                                       return false;
+                               }
+                       }
+               }
+               
+               // rgb(100%,100%,100%)
+               // rgba(100%,100%,100%,100%)
+               // rgba(255,255,255,1)
+               // rgba(100%,1, 0.5,.2)
+               else if(
+                       val.match(/^rgba\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ||
+                       val.match(/^rgb\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null 
+               ) {
+                       if(val.match(/a/i) != null) {
+                               hasAlpha = true;
+                       }
+                       else {
+                               hasAlpha = false;
+                       }
+                       
+                       tmp = val.substring(val.indexOf('(')+1, val.indexOf(','));
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.r = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.r = parseInt(tmp) / 255 ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       tmp = val.substring(val.indexOf(',')+1, val.indexOf(',', val.indexOf(',')+1));
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.g = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.g = parseInt(tmp) / 255 ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       if(hasAlpha) {
+                               tmp = val.substring(val.indexOf(',', val.indexOf(',')+1)+1, val.lastIndexOf(','));
+                       }
+                       else {
+                               tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')'));
+                       }
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.b = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.b = parseInt(tmp) / 255 ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       if(hasAlpha) {
+                               tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')'));
+                               if( tmp.charAt( tmp.length-1 ) == '%') {
+                                       if( isNaN( color.a = parseFloat(tmp) / 100 ) ) {
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       if( isNaN( color.a = parseFloat(tmp) ) ) {
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+               
+               // hsv(100%,100%,100%)
+               // hsva(100%,100%,100%,100%)
+               // hsv(360,1,1,1)
+               // hsva(360,1, 0.5,.2)
+               // hsb(100%,100%,100%)
+               // hsba(100%,100%,100%,100%)
+               // hsb(360,1,1,1)
+               // hsba(360,1, 0.5,.2)
+               else if(
+                       val.match(/^hsva\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ||
+                       val.match(/^hsv\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ||
+                       val.match(/^hsba\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null ||
+                       val.match(/^hsb\s*\(\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*,\s*([0-9\.]+%|[01]?\.?[0-9]*)\s*\)$/i) != null 
+               ) {
+                       if(val.match(/a/i) != null) {
+                               hasAlpha = true;
+                       }
+                       else {
+                               hasAlpha = false;
+                       }
+                       
+                       tmp = val.substring(val.indexOf('(')+1, val.indexOf(','));
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.h = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.h = parseFloat(tmp) / 360 ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       tmp = val.substring(val.indexOf(',')+1, val.indexOf(',', val.indexOf(',')+1));
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.s = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.s = parseFloat(tmp) ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       if(hasAlpha) {
+                               tmp = val.substring(val.indexOf(',', val.indexOf(',')+1)+1, val.lastIndexOf(','));
+                       }
+                       else {
+                               tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')'));
+                       }
+                       if( tmp.charAt( tmp.length-1 ) == '%') {
+                               if( isNaN( color.v = parseFloat(tmp) / 100 ) ) {
+                                       return false;
+                               }
+                       }
+                       else {
+                               if( isNaN( color.v = parseFloat(tmp) ) ) {
+                                       return false;
+                               }
+                       }
+                       
+                       if(hasAlpha) {
+                               tmp = val.substring(val.lastIndexOf(',')+1, val.lastIndexOf(')'));
+                               if( tmp.charAt( tmp.length-1 ) == '%') {
+                                       if( isNaN( color.a = parseFloat(tmp) / 100 ) ) {
+                                               return false;
+                                       }
+                               }
+                               else {
+                                       if( isNaN( color.a = parseFloat(tmp) ) ) {
+                                               return false;
+                                       }
+                               }
+                       }
+               }
+               
+               else {
+                       return false;
+               }
+               
+               return color;
+       };
+
+
+       /**
+        * Function: hsvToRgb
+        * 
+        * Since 2.0
+        * 
+        * Perform HSV to RGB conversion
+        */
+       WCP.hsvToRgb = function( h, s, v ) {
+               
+               // Calculate RGB from hue (1st phase)
+               var cr = (h <= (1/6) || h >= (5/6)) ? 1
+                       : (h < (1/3) ? 1 - ((h - (1/6)) * 6)
+                       : (h > (4/6) ? (h - (4/6)) * 6
+                       : 0));
+               var cg = (h >= (1/6) && h <= (3/6)) ? 1
+                       : (h < (1/6) ? h * 6
+                       : (h < (4/6) ? 1 - ((h - (3/6)) * 6)
+                       : 0));
+               var cb = (h >= (3/6) && h <= (5/6)) ? 1
+                       : (h > (2/6) && h < (3/6) ? (h - (2/6)) * 6
+                       : (h > (5/6) ? 1 - ((h - (5/6)) * 6)
+                       : 0));
+                       
+               //~ console.log(cr + ' ' + cg + ' ' + cb);
+               
+               // Calculate RGB with saturation & value applied
+               var r = (cr + (1-cr)*(1-s)) * v;
+               var g = (cg + (1-cg)*(1-s)) * v;
+               var b = (cb + (1-cb)*(1-s)) * v;
+               
+               //~ console.log(r + ' ' + g + ' ' + b + ' ' + v);
+               
+               return { r: r, g: g, b: b };
+       };
+
+
+       /**
+        * Function: rgbToHsv
+        * 
+        * Since 2.0
+        * 
+        * Perform RGB to HSV conversion
+        */
+       WCP.rgbToHsv = function( r, g, b ) {
+               
+               var h;
+               var s;
+               var v;
+
+               var maxColor = Math.max(r, g, b);
+               var minColor = Math.min(r, g, b);
+               var delta = maxColor - minColor;
+               
+               // Calculate saturation
+               if(maxColor != 0) {
+                       s = delta / maxColor;
+               }
+               else {
+                       s = 0;
+               }
+               
+               // Calculate hue
+               // To simplify the formula, we use 0-6 range.
+               if(delta == 0) {
+                       h = 0;
+               }
+               else if(r == maxColor) {
+                       h = (6 + (g - b) / delta) % 6;
+               }
+               else if(g == maxColor) {
+                       h = 2 + (b - r) / delta;
+               }
+               else if(b == maxColor) {
+                       h = 4 + (r - g) / delta;
+               }
+               else {
+                       h = 0;
+               }
+               // Then adjust the range to be 0-1
+               h = h/6;
+               
+               // Calculate value
+               v = maxColor;
+               
+               //~ console.log(h + ' ' + s + ' ' + v);
+               
+               return { h: h, s: s, v: v };
+       };
+
+
+
+       /******************************************************************/
+
+       ////////////////////////
+       // COLOR PICKER CLASS //
+       ////////////////////////
+
+       /**
+        * Class: ColorPicker
+        * 
+        * Since 3.0
+        */
+       WCP.ColorPicker = function ( elm, options ) {
+               
+               // Assign reference to input DOM element
+               this.input = elm;
+               
+               // Setup selected color in the following priority:
+               // 1. options.color
+               // 2. input.value
+               // 3. default
+               this.color = { h: 0, s: 0, v: 1, r: 1, g: 1, b: 1, a: 1 };
+               this.setValue(this.input.value);
+               
+               // Set options
+               this.options = $.extend(true, {}, WCP.defaults);
+               this.setOptions(options);
+               
+               // Check sliders option, if not defined, set default sliders
+               if(this.options.sliders == null)
+                       this.options.sliders = 'wvp' + (this.options.format.indexOf('a') >= 0 ? 'a' : '');
+               
+               this.init();
+       };
+
+
+       ////////////////////
+       // Static members //
+       ////////////////////
+
+
+       /**
+        * Static Property: ColorPicker.widget
+        * 
+        * Reference to global color picker widget (popup)
+        */
+       WCP.ColorPicker.widget = null;
+
+
+       /**
+        * Property: ColorPicker.overlay
+        * 
+        * Reference to overlay DOM element (overlay for global popup)
+        */
+       WCP.ColorPicker.overlay = null;
+
+
+       /**
+        * Function: init
+        * 
+        * Since 3.0
+        * 2.0 was methods.staticInit
+        * 
+        * Initialize wheel color picker globally.
+        */
+       WCP.ColorPicker.init = function() {
+               // Only perform initialization once
+               if(WCP.ColorPicker.init.hasInit == true)
+                       return;
+               WCP.ColorPicker.init.hasInit = true;
+                       
+               // Insert overlay element to handle popup closing
+               // when hideKeyboard is true, hence input is always blurred
+               var $overlay = $('<div class="jQWCP-overlay" style="display: none;"></div>');
+               $overlay.on('click', WCP.Handler.overlay_click);
+               WCP.ColorPicker.overlay = $overlay.get(0);
+               $('body').append($overlay);
+        
+        // Insert CSS for color wheel
+        var wheelImage = WCP.ColorPicker.getWheelDataUrl(200);
+        $('head').append(
+            '<style type="text/css">' + 
+                '.jQWCP-wWheel {' + 
+                    'background-image: url(' + wheelImage + ');' +
+                '}' +
+            '</style>'
+        );
+               
+               // Attach events
+               $('html').on('mouseup.wheelColorPicker', WCP.Handler.html_mouseup);
+               $('html').on('touchend.wheelColorPicker', WCP.Handler.html_mouseup);
+               $('html').on('mousemove.wheelColorPicker', WCP.Handler.html_mousemove);
+               $('html').on('touchmove.wheelColorPicker', WCP.Handler.html_mousemove);
+        $(window).on('resize.wheelColorPicker', WCP.Handler.window_resize);
+       };
+
+
+       /**
+        * Function: createWidget
+        * 
+        * Since 3.0
+        * 2.5 was private.initWidget
+        * 
+        * Create color picker widget.
+        */
+       WCP.ColorPicker.createWidget = function() {
+               /// WIDGET ///
+               // Notice: We won't use canvas to draw the color wheel since 
+               // it may takes time and cause performance issue.
+               var $widget = $(
+                       "<div class='jQWCP-wWidget'>" + 
+                               "<div class='jQWCP-wWheel'>" + 
+                                       "<div class='jQWCP-wWheelOverlay'></div>" +
+                                       "<span class='jQWCP-wWheelCursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wHue jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wHueSlider jQWCP-slider' width='1' height='50' title='Hue'></canvas>" +
+                                       "<span class='jQWCP-wHueCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wSat jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wSatSlider jQWCP-slider' width='1' height='50' title='Saturation'></canvas>" +
+                                       "<span class='jQWCP-wSatCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wVal jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wValSlider jQWCP-slider' width='1' height='50' title='Value'></canvas>" +
+                                       "<span class='jQWCP-wValCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wRed jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wRedSlider jQWCP-slider' width='1' height='50' title='Red'></canvas>" +
+                                       "<span class='jQWCP-wRedCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wGreen jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wGreenSlider jQWCP-slider' width='1' height='50' title='Green'></canvas>" +
+                                       "<span class='jQWCP-wGreenCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wBlue jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wBlueSlider jQWCP-slider' width='1' height='50' title='Blue'></canvas>" +
+                                       "<span class='jQWCP-wBlueCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wAlpha jQWCP-slider-wrapper'>" +
+                                       "<canvas class='jQWCP-wAlphaSlider jQWCP-slider' width='1' height='50' title='Alpha'></canvas>" +
+                                       "<span class='jQWCP-wAlphaCursor jQWCP-scursor'></span>" +
+                               "</div>" +
+                               "<div class='jQWCP-wPreview'>" +
+                                       "<canvas class='jQWCP-wPreviewBox' width='1' height='1' title='Selected Color'></canvas>" +
+                               "</div>" +
+                       "</div>"
+               );
+                       
+               // Small UI fix to disable highlighting the widget
+               // Also UI fix to disable touch context menu 
+               $widget.find('.jQWCP-wWheel, .jQWCP-slider-wrapper, .jQWCP-scursor, .jQWCP-slider')
+                       .attr('unselectable', 'on')
+                       .css('-moz-user-select', 'none')
+                       .css('-webkit-user-select', 'none')
+                       .css('user-select', 'none')
+                       .css('-webkit-touch-callout', 'none');
+                       
+               // Disable context menu on sliders
+               // Workaround for touch browsers
+               $widget.on('contextmenu.wheelColorPicker', function() { return false; });
+                       
+               // Bind widget events
+               $widget.on('mousedown.wheelColorPicker', '.jQWCP-wWheel', WCP.Handler.wheel_mousedown);
+               $widget.on('touchstart.wheelColorPicker', '.jQWCP-wWheel', WCP.Handler.wheel_mousedown);
+               $widget.on('mousedown.wheelColorPicker', '.jQWCP-wWheelCursor', WCP.Handler.wheelCursor_mousedown);
+               $widget.on('touchstart.wheelColorPicker', '.jQWCP-wWheelCursor', WCP.Handler.wheelCursor_mousedown);
+               $widget.on('mousedown.wheelColorPicker', '.jQWCP-slider', WCP.Handler.slider_mousedown);
+               $widget.on('touchstart.wheelColorPicker', '.jQWCP-slider', WCP.Handler.slider_mousedown);
+               $widget.on('mousedown.wheelColorPicker', '.jQWCP-scursor', WCP.Handler.sliderCursor_mousedown);
+               $widget.on('touchstart.wheelColorPicker', '.jQWCP-scursor', WCP.Handler.sliderCursor_mousedown);
+               
+               return $widget.get(0);
+       };
+    
+    
+    /**
+     * Function: getWheelDataUrl
+     * 
+     * Create color wheel image and return as base64 encoded data url.
+     */
+    WCP.ColorPicker.getWheelDataUrl = function( size ) {
+        var r = size / 2; // radius
+        var center = r;
+        var canvas = document.createElement('canvas');
+        canvas.width = size;
+        canvas.height = size;
+        var context = canvas.getContext('2d');
+        
+        // Fill the wheel with colors
+        for(var y = 0; y < size; y++) {
+            for(var x = 0; x < size; x++) {
+                // Get the offset from central position
+                var offset = Math.sqrt(Math.pow(x - center, 2) + Math.pow(y - center, 2));
+                
+                // Skip pixels outside picture area (plus 2 pixels)
+                if(offset > r + 2) {
+                    continue;
+                }
+                
+                // Get the position in degree (hue)
+                var deg = (
+                    (x - center == 0 
+                        ? (y < center ? 90 : 270)
+                        : (Math.atan((center - y) / (x - center)) / Math.PI * 180)
+                    )
+                    + (x < center ? 180 : 0)
+                    + 360
+                ) % 360;
+                
+                // Relative offset (sat)
+                var sat = offset / r;
+                
+                // Value is always 1
+                var val = 1;
+                
+                // Calculate color
+                var cr = (Math.abs(deg + 360) + 60) % 360 < 120 ? 1
+                    : (deg > 240 ? (120 - Math.abs(deg - 360)) / 60
+                    : (deg < 120 ? (120 - deg) / 60
+                    : 0));
+                var cg = Math.abs(deg - 120) < 60 ? 1
+                    : (Math.abs(deg - 120) < 120 ? (120 - Math.abs(deg - 120)) / 60
+                    : 0);
+                
+                var cb = Math.abs(deg - 240) < 60 ? 1
+                    : (Math.abs(deg - 240) < 120 ? (120 - Math.abs(deg - 240)) / 60
+                    : 0);
+                var pr = Math.round((cr + (1 - cr) * (1 - sat)) * 255);
+                var pg = Math.round((cg + (1 - cg) * (1 - sat)) * 255);
+                var pb = Math.round((cb + (1 - cb) * (1 - sat)) * 255);
+                
+                context.fillStyle = 'rgb(' + pr + ',' + pg + ',' + pb + ')';
+                context.fillRect(x, y, 1, 1);
+            }
+        }
+        
+        return canvas.toDataURL();
+    };
+
+
+       /////////////
+       // Members //
+       /////////////
+
+
+       /**
+        * Property: ColorPicker.options
+        * 
+        * Plugin options for the color picker instance, extended from WCP.defaults.
+        */
+       WCP.ColorPicker.prototype.options = null;
+
+
+       /**
+        * Property: ColorPicker.input
+        * 
+        * Reference to input DOM element
+        */
+       WCP.ColorPicker.prototype.input = null;
+
+
+       /** 
+        * Property: ColorPicker.widget
+        * 
+        * Reference to widget DOM element (global popup or private inline widget)
+        */
+       WCP.ColorPicker.prototype.widget = null;
+
+
+       /**
+        * Property: ColorPicker.color
+        * 
+        * Selected color object.
+        */
+       WCP.ColorPicker.prototype.color = null;
+
+
+       /**
+        * Property: ColorPicker.lastValue
+        * 
+        * Store last input value
+        */
+       WCP.ColorPicker.prototype.lastValue = null;
+
+
+       /**
+        * Function: ColorPicker.setOptions
+        * 
+        * Since 3.0
+        * 
+        * Set options to the color picker. If htmlOptions is set to true, 
+        * options set via html attributes are also reloaded. If both html 
+        * attribute and argument exists, option set via options argument 
+        * gets priority.
+        */
+       WCP.ColorPicker.prototype.setOptions = function( options ) {
+               
+               // options should be a separate object (passed by value)
+               // Make a copy of options
+               options = $.extend(true, {}, options);
+               
+               // Load options from HTML attributes
+               if(this.options.htmlOptions) {
+                       for(var key in WCP.defaults) {
+                               // Only if option key is valid and not set via function argument
+                               if(this.input.hasAttribute('data-wcp-'+key) && options[key] === undefined) {
+                                       options[key] = this.input.getAttribute('data-wcp-'+key);
+                                       // Change true/false string to boolean
+                                       if(options[key] == 'true') {
+                                               options[key] = true;
+                                       }
+                                       else if(options[key] == 'false') {
+                                               options[key] = false;
+                                       }
+                               }
+                       }
+               }
+               
+               // Set options
+               for(var key in options) {
+                       // Skip undefined option key
+                       if(this.options[key] === undefined)
+                               continue;
+                       
+                       var keyUc = key.charAt(0).toUpperCase() + key.slice(1);
+                       
+                       // If setter is available, try setting it via setter
+                       if(typeof this['set'+keyUc] === "function") {
+                               this['set'+keyUc](options[key]);
+                       }
+                       // Otherwise directly update options
+                       else {
+                               this.options[key] = options[key];
+                       }
+               }
+               
+               return this; // Allow chaining
+       };
+
+
+       /**
+        * Function: ColorPicker.init
+        * 
+        * Initialize wheel color picker widget
+        */
+       WCP.ColorPicker.prototype.init = function() {
+               WCP.ColorPicker.init();
+
+               // Initialization must only occur once
+               if(this.hasInit == true)
+                       return;
+               this.hasInit = true;
+               
+               var instance = this;
+               var $input = $(this.input);
+               var $widget = null;
+               
+               /// LAYOUT & BINDINGS ///
+               // Setup block mode layout
+               if( this.options.layout == 'block' ) {
+                       // Create widget
+                       this.widget = WCP.ColorPicker.createWidget();
+                       $widget = $(this.widget);
+                       
+                       // Store object instance reference
+                       $widget.data('jQWCP.instance', this);
+                       
+                       // Wrap widget around the input elm and put the input 
+                       // elm inside widget
+                       $widget.insertAfter(this.input);
+                       // Retain display CSS property
+                       if($input.css('display') == "inline") {
+                               $widget.css('display', "inline-block");
+                       }
+                       else {
+                               $widget.css('display', $input.css('display'));
+                       }
+                       $widget.append(this.input);
+                       $input.hide();
+                       
+                       // Add tabindex attribute to make the widget focusable
+                       if($input.attr('tabindex') != undefined) {
+                               $widget.attr('tabindex', $input.attr('tabindex'));
+                       }
+                       else {
+                               $widget.attr('tabindex', 0);
+                       }
+                       
+                       // Further widget adjustments based on options
+                       this.refreshWidget();
+                       
+                       // Draw shading
+                       this.redrawSliders(true);
+                       this.updateSliders();
+                       
+                       // Bind widget element events
+                       $widget.on('focus.wheelColorPicker', WCP.Handler.widget_focus_block);
+                       $widget.on('blur.wheelColorPicker', WCP.Handler.widget_blur_block);
+               }
+               
+               // Setup popup mode layout
+               else {
+                       // Only need to create one widget, used globally
+                       if(WCP.ColorPicker.widget == null) {
+                               WCP.ColorPicker.widget = WCP.ColorPicker.createWidget();
+                               $widget = $(WCP.ColorPicker.widget);
+                               
+                               // Assign widget to global
+                               $widget.hide();
+                               $('body').append($widget);
+                               
+                               // Bind popup events
+                               $widget.on('mousedown.wheelColorPicker', WCP.Handler.widget_mousedown_popup);
+                               //$widget.on('mouseup.wheelColorPicker', WCP.Handler.widget_mouseup_popup);
+                               
+                       }
+                       this.widget = WCP.ColorPicker.widget;
+                       
+                       // Bind input element events
+                       $input.on('focus.wheelColorPicker', WCP.Handler.input_focus_popup);
+                       $input.on('blur.wheelColorPicker', WCP.Handler.input_blur_popup);
+               }
+               
+               // Bind input events
+               $input.on('keyup.wheelColorPicker', WCP.Handler.input_keyup);
+               $input.on('change.wheelColorPicker', WCP.Handler.input_change);
+               
+               // Set color value
+               // DEPRECATED by 3.0
+               if(typeof this.options.color == "object") {
+                       this.setColor(this.options.color);
+                       this.options.color = undefined;
+               }
+               else if(typeof this.options.color == "string") {
+                       this.setValue(this.options.color);
+                       this.options.color = undefined;
+               }
+               
+               // Set readonly mode
+               /* DEPRECATED */
+               if(this.options.userinput) {
+                       $input.removeAttr('readonly');
+               }
+               else {
+                       $input.attr('readonly', true);
+               }
+       };
+
+
+       /**
+        * Function: destroy
+        * 
+        * Destroy the color picker and return it to normal element.
+        */
+       WCP.ColorPicker.prototype.destroy = function() {
+               var $widget = $(this.widget);
+               var $input = $(this.input);
+               
+               // Reset layout
+               // No need to delete global popup
+               if(this.options.layout == 'block') {
+                       // Check if active control is the same widget as destroyed widget, remove the reference if it's true
+                       var $control = $( $('body').data('jQWCP.activeControl') ); // Refers to slider wrapper or wheel
+                       if ($control.length) {
+                               var controlWidget = $control.closest('.jQWCP-wWidget');
+                               if ($widget.is(controlWidget)) {
+                                       $('body').data('jQWCP.activeControl', null);
+                               }
+                       }
+                       
+                       $widget.before(this.input);
+                       $widget.remove();
+                       $input.show();
+               }
+               
+               // Unbind events
+               $input.off('focus.wheelColorPicker');
+               $input.off('blur.wheelColorPicker');
+               $input.off('keyup.wheelColorPicker');
+               $input.off('change.wheelColorPicker');
+               
+               // Remove data
+               $input.data('jQWCP.instance', null);
+               
+               // remove self
+               delete this;
+       };
+
+
+       /**
+        * Function: refreshWidget
+        * 
+        * Since 3.0
+        * 2.5 was private.adjustWidget
+        * 
+        * Update widget to match current option values.
+        */
+       WCP.ColorPicker.prototype.refreshWidget = function() {
+               var $widget = $(this.widget);
+               var options = this.options;
+        var mobileLayout = false;
+               
+               // Set CSS classes
+               $widget.attr('class', 'jQWCP-wWidget');
+               if(options.layout == 'block') {
+                       $widget.addClass('jQWCP-block');
+               }
+               $widget.addClass(options.cssClass);
+               //$widget.addClass(this.input.getAttribute('class'));
+        
+        // Check whether to use mobile layout
+        if(window.innerWidth <= options.mobileWidth && options.layout != 'block' && options.mobile) {
+            mobileLayout = true;
+            $widget.addClass('jQWCP-mobile');
+        }
+               
+               // Rearrange sliders
+               $widget.find('.jQWCP-wWheel, .jQWCP-slider-wrapper, .jQWCP-wPreview')
+                       .hide()
+                       .addClass('hidden');
+                       
+               for(var i in options.sliders) {
+                       var $slider = null;
+                       switch(this.options.sliders[i]) {
+                               case 'w':
+                                       $slider = $widget.find('.jQWCP-wWheel'); break;
+                               case 'h':
+                                       $slider = $widget.find('.jQWCP-wHue'); break;
+                               case 's':
+                                       $slider = $widget.find('.jQWCP-wSat'); break;
+                               case 'v':
+                                       $slider = $widget.find('.jQWCP-wVal'); break;
+                               case 'r':
+                                       $slider = $widget.find('.jQWCP-wRed'); break;
+                               case 'g':
+                                       $slider = $widget.find('.jQWCP-wGreen'); break;
+                               case 'b':
+                                       $slider = $widget.find('.jQWCP-wBlue'); break;
+                               case 'a':
+                                       $slider = $widget.find('.jQWCP-wAlpha'); break;
+                               case 'p':
+                                       $slider = $widget.find('.jQWCP-wPreview'); break;
+                       }
+                       
+                       if($slider != null) {
+                               $slider.appendTo(this.widget);
+                               $slider.show().removeClass('hidden');
+                       }
+               }
+               
+               // If widget is hidden, show it first so we can calculate dimensions correctly
+               //var widgetIsHidden = false;
+               //if($widget.is(':hidden')) {
+                       //widgetIsHidden = true;
+                       //$widget.css({ opacity: '0' }).show();
+               //}
+               
+               // Adjust sliders height based on quality
+               var sliderHeight = options.quality * 50;
+               $widget.find('.jQWCP-slider').attr('height', sliderHeight);
+               
+               var $visElms = $widget.find('.jQWCP-wWheel, .jQWCP-slider-wrapper, .jQWCP-wPreview').not('.hidden');
+                       
+               // Adjust container and sliders width
+        // Only if not on mobile layout (force fixed on mobile)
+               if(options.autoResize && !mobileLayout) {
+                       // Auto resize
+                       var width = 0
+                       
+                       // Set slider size first, then adjust container
+                       $visElms.css({ width: '', height: '' });
+                       
+                       $visElms.each(function(index, item) {
+                               var $item = $(item);
+                               width += parseFloat($item.css('margin-left').replace('px', '')) + parseFloat($item.css('margin-right').replace('px', '')) + $item.outerWidth();
+                       });
+                       $widget.css({ width: width + 'px' });
+               }
+               else {
+                       // Fixed size
+                       
+                       // Set container size first, then adjust sliders
+                       $widget.css({ width: '' });
+                       
+                       var $visWheel = $widget.find('.jQWCP-wWheel').not('.hidden');
+                       var $visSliders = $widget.find('.jQWCP-slider-wrapper, .jQWCP-wPreview').not('.hidden');
+                       $visWheel.css({ height: $widget.height() + 'px', width: $widget.height() });
+                       if($visWheel.length > 0) {
+                               var horzSpace = $widget.width() - $visWheel.outerWidth() - parseFloat($visWheel.css('margin-left').replace('px', '')) - parseFloat($visWheel.css('margin-right').replace('px', ''));
+                       }
+                       else {
+                               var horzSpace = $widget.width();
+                       }
+                       if($visSliders.length > 0) {
+                               var sliderMargins = parseFloat($visSliders.css('margin-left').replace('px', '')) + parseFloat($visSliders.css('margin-right').replace('px', ''));
+                               $visSliders.css({ height: $widget.height() + 'px', width: (horzSpace - ($visSliders.length - 1) * sliderMargins) / $visSliders.length + 'px' });
+                       }
+               }
+               
+               // Reset visibility
+               //if(widgetIsHidden) {
+                       //$widget.css({ opacity: '' }).hide();
+               //}
+               
+               return this; // Allows method chaining
+       };
+
+
+       /**
+        * Function: redrawSliders
+        * 
+        * Introduced in 2.0
+        * 
+        * Redraw slider gradients. Hidden sliders are not redrawn as to 
+        * improve performance. If options.live is FALSE, sliders are not redrawn.
+        * 
+        * Parameter:
+        *   force   - Boolean force redraw all sliders.
+        */
+       WCP.ColorPicker.prototype.redrawSliders = function( force ) {
+               
+               // Skip if widget not yet initialized
+               if(this.widget == null) 
+                       return this;
+               
+               var $widget = $(this.widget);
+               
+               // DEPRECATED 3.0
+               // In 2.0, parameters are ( sliders, force )
+               if(typeof arguments[0] === "string") {
+                       force = arguments[1];
+               }
+               
+               // No need to redraw sliders on global popup widget if not 
+               // attached to the input elm in current iteration
+               if(this != $widget.data('jQWCP.instance'))
+                       return this;
+                       
+               var options = this.options;
+               var color = this.color;
+                       
+               var w = 1;
+               var h = options.quality * 50;
+               
+               var A = 1;
+               var R = 0;
+               var G = 0;
+               var B = 0;
+               var H = 0;
+               var S = 0;
+               var V = 1;
+               
+               // Dynamic colors
+               if(options.live) {
+                       A = color.a;
+                       R = Math.round(color.r * 255);
+                       G = Math.round(color.g * 255);
+                       B = Math.round(color.b * 255);
+                       H = color.h;
+                       S = color.s;
+                       V = color.v;
+               }
+               
+               /// PREVIEW ///
+               // Preview box must always be redrawn, if not hidden
+               var $previewBox = $widget.find('.jQWCP-wPreviewBox');
+               if(!$previewBox.hasClass('hidden')) {
+                       var previewBoxCtx = $previewBox.get(0).getContext('2d');
+                       previewBoxCtx.fillStyle = "rgba(" + R + "," + G + "," + B + "," + A + ")";
+                       previewBoxCtx.clearRect(0, 0, 1, 1);
+                       previewBoxCtx.fillRect(0, 0, 1, 1);
+               }
+               
+               /// SLIDERS ///
+               if(!this.options.live && !force)
+                       return this;
+               
+               /// ALPHA ///
+               // The top color is (R, G, B, 1)
+               // The bottom color is (R, G, B, 0)
+               var $alphaSlider = $widget.find('.jQWCP-wAlphaSlider');
+               if(!$alphaSlider.hasClass('hidden') || force) {
+                       var alphaSliderCtx = $alphaSlider.get(0).getContext('2d');
+                       var alphaGradient = alphaSliderCtx.createLinearGradient(0, 0, 0, h);
+                       alphaGradient.addColorStop(0, "rgba("+R+","+G+","+B+",1)");
+                       alphaGradient.addColorStop(1, "rgba("+R+","+G+","+B+",0)");
+                       alphaSliderCtx.fillStyle = alphaGradient;
+                       alphaSliderCtx.clearRect(0, 0, w, h);
+                       alphaSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// RED ///
+               // The top color is (255, G, B)
+               // The bottom color is (0, G, B)
+               var $redSlider = $widget.find('.jQWCP-wRedSlider');
+               if(!$redSlider.hasClass('hidden') || force) {
+                       var redSliderCtx = $redSlider.get(0).getContext('2d');
+                       var redGradient = redSliderCtx.createLinearGradient(0, 0, 0, h);
+                       redGradient.addColorStop(0, "rgb(255,"+G+","+B+")");
+                       redGradient.addColorStop(1, "rgb(0,"+G+","+B+")");
+                       redSliderCtx.fillStyle = redGradient;
+                       redSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// GREEN ///
+               // The top color is (R, 255, B)
+               // The bottom color is (R, 0, B)
+               var $greenSlider = $widget.find('.jQWCP-wGreenSlider');
+               if(!$greenSlider.hasClass('hidden') || force) {
+                       var greenSliderCtx = $greenSlider.get(0).getContext('2d');
+                       var greenGradient = greenSliderCtx.createLinearGradient(0, 0, 0, h);
+                       greenGradient.addColorStop(0, "rgb("+R+",255,"+B+")");
+                       greenGradient.addColorStop(1, "rgb("+R+",0,"+B+")");
+                       greenSliderCtx.fillStyle = greenGradient;
+                       greenSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// BLUE ///
+               // The top color is (R, G, 255)
+               // The bottom color is (R, G, 0)
+               var $blueSlider = $widget.find('.jQWCP-wBlueSlider');
+               if(!$blueSlider.hasClass('hidden') || force) {
+                       var blueSliderCtx = $blueSlider.get(0).getContext('2d');
+                       var blueGradient = blueSliderCtx.createLinearGradient(0, 0, 0, h);
+                       blueGradient.addColorStop(0, "rgb("+R+","+G+",255)");
+                       blueGradient.addColorStop(1, "rgb("+R+","+G+",0)");
+                       blueSliderCtx.fillStyle = blueGradient;
+                       blueSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// HUE ///
+               // The hue slider is static.
+               var $hueSlider = $widget.find('.jQWCP-wHueSlider');
+               if(!$hueSlider.hasClass('hidden') || force) {
+                       var hueSliderCtx = $hueSlider.get(0).getContext('2d');
+                       var hueGradient = hueSliderCtx.createLinearGradient(0, 0, 0, h);
+                       hueGradient.addColorStop(0, "#f00");
+                       hueGradient.addColorStop(0.166666667, "#ff0");
+                       hueGradient.addColorStop(0.333333333, "#0f0");
+                       hueGradient.addColorStop(0.5, "#0ff");
+                       hueGradient.addColorStop(0.666666667, "#00f");
+                       hueGradient.addColorStop(0.833333333, "#f0f");
+                       hueGradient.addColorStop(1, "#f00");
+                       hueSliderCtx.fillStyle = hueGradient;
+                       hueSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// SAT ///
+               // The top color is hsv(h, 1, v)
+               // The bottom color is hsv(0, 0, v)
+               var $satSlider = $widget.find('.jQWCP-wSatSlider');
+               if(!$satSlider.hasClass('hidden') || force) {
+                       var satTopRgb = $.fn.wheelColorPicker.hsvToRgb(H, 1, V);
+                       satTopRgb.r = Math.round(satTopRgb.r * 255);
+                       satTopRgb.g = Math.round(satTopRgb.g * 255);
+                       satTopRgb.b = Math.round(satTopRgb.b * 255);
+                       var satSliderCtx = $satSlider.get(0).getContext('2d');
+                       var satGradient = satSliderCtx.createLinearGradient(0, 0, 0, h);
+                       satGradient.addColorStop(0, "rgb("+satTopRgb.r+","+satTopRgb.g+","+satTopRgb.b+")");
+                       satGradient.addColorStop(1, "rgb("+Math.round(V*255)+","+Math.round(V*255)+","+Math.round(V*255)+")");
+                       satSliderCtx.fillStyle = satGradient;
+                       satSliderCtx.fillRect(0, 0, w, h);
+               }
+               
+               /// VAL ///
+               // The top color is hsv(h, s, 1)
+               // The bottom color is always black.
+               var $valSlider = $widget.find('.jQWCP-wValSlider');
+               if(!$valSlider.hasClass('hidden') || force) {
+                       var valTopRgb = $.fn.wheelColorPicker.hsvToRgb(H, S, 1);
+                       valTopRgb.r = Math.round(valTopRgb.r * 255);
+                       valTopRgb.g = Math.round(valTopRgb.g * 255);
+                       valTopRgb.b = Math.round(valTopRgb.b * 255);
+                       var valSliderCtx = $valSlider.get(0).getContext('2d');
+                       var valGradient = valSliderCtx.createLinearGradient(0, 0, 0, h);
+                       valGradient.addColorStop(0, "rgb("+valTopRgb.r+","+valTopRgb.g+","+valTopRgb.b+")");
+                       valGradient.addColorStop(1, "#000");
+                       valSliderCtx.fillStyle = valGradient;
+                       valSliderCtx.fillRect(0, 0, w, h);
+               }
+                       
+               return this; // Allows method chaining
+       };
+
+
+       /**
+        * Function: updateSliders
+        * 
+        * Introduced in 2.0
+        * 
+        * Update slider cursor positions based on this.color value. 
+        * Only displayed sliders are updated.
+        */
+       WCP.ColorPicker.prototype.updateSliders = function() {
+               
+               // Skip if not yet initialized
+               if(this.widget == null)
+                       return this;
+                       
+               var $widget = $(this.widget);
+               var color = this.color;
+               
+               // No need to redraw sliders on global popup widget if not 
+               // attached to the input elm in current iteration
+               if(this != $widget.data('jQWCP.instance'))
+                       return this;
+                       
+               // Wheel
+               var $wheel = $widget.find('.jQWCP-wWheel');
+               if(!$wheel.hasClass('hidden')) {
+                       var $wheelCursor = $widget.find('.jQWCP-wWheelCursor');
+                       var $wheelOverlay = $widget.find('.jQWCP-wWheelOverlay');
+                       var wheelX = Math.cos(2 * Math.PI * color.h) * color.s;
+                       var wheelY = Math.sin(2 * Math.PI * color.h) * color.s;
+                       var wheelOffsetX = $wheel.width() / 2;
+                       var wheelOffsetY = $wheel.height() / 2;
+                       $wheelCursor.css('left', (wheelOffsetX + (wheelX * $wheel.width() / 2)) + 'px');
+                       $wheelCursor.css('top', (wheelOffsetY - (wheelY * $wheel.height() / 2)) + 'px');
+                       // Keep shading to 1 if preserveWheel is true (DEPRECATED) or live is true
+                       if(this.options.preserveWheel == true || (this.options.preserveWheel == null && this.options.live == false)) {
+                               $wheelOverlay.css('opacity', 0);
+                       }
+                       else {
+                               $wheelOverlay.css('opacity', 1 - (color.v < 0.2 ? 0.2 : color.v));
+                       }
+               }
+               
+               // Hue
+               var $hueSlider = $widget.find('.jQWCP-wHueSlider');
+               if(!$hueSlider.hasClass('hidden')) {
+                       var $hueCursor = $widget.find('.jQWCP-wHueCursor');
+                       $hueCursor.css('top', (color.h * $hueSlider.height()) + 'px');
+               }
+               
+               // Saturation
+               var $satSlider = $widget.find('.jQWCP-wSatSlider');
+               if(!$satSlider.hasClass('hidden')) {
+                       var $satCursor = $widget.find('.jQWCP-wSatCursor');
+                       $satCursor.css('top', ((1 - color.s) * $satSlider.height()) + 'px');
+               }
+               
+               // Value
+               var $valSlider = $widget.find('.jQWCP-wValSlider');
+               if(!$valSlider.hasClass('hidden')) {
+                       var $valCursor = $widget.find('.jQWCP-wValCursor');
+                       $valCursor.css('top', ((1 - color.v) * $valSlider.height()) + 'px');
+               }
+               
+               // Red
+               var $redSlider = $widget.find('.jQWCP-wRedSlider');
+               if(!$redSlider.hasClass('hidden')) {
+                       var $redCursor = $widget.find('.jQWCP-wRedCursor');
+                       $redCursor.css('top', ((1 - color.r) * $redSlider.height()) + 'px');
+               }
+               
+               // Green
+               var $greenSlider = $widget.find('.jQWCP-wGreenSlider');
+               if(!$greenSlider.hasClass('hidden')) {
+                       var $greenCursor = $widget.find('.jQWCP-wGreenCursor');
+                       $greenCursor.css('top', ((1 - color.g) * $greenSlider.height()) + 'px');
+               }
+               
+               // Blue
+               var $blueSlider = $widget.find('.jQWCP-wBlueSlider');
+               if(!$blueSlider.hasClass('hidden')) {
+                       var $blueCursor = $widget.find('.jQWCP-wBlueCursor');
+                       $blueCursor.css('top', ((1 - color.b) * $blueSlider.height()) + 'px');
+               }
+               
+               // Alpha
+               var $alphaSlider = $widget.find('.jQWCP-wAlphaSlider');
+               if(!$alphaSlider.hasClass('hidden')) {
+                       var $alphaCursor = $widget.find('.jQWCP-wAlphaCursor');
+                       $alphaCursor.css('top', ((1 - color.a) * $alphaSlider.height()) + 'px');
+               }
+                       
+               return this; // Allows method chaining
+       };
+
+
+       /**
+        * Function: updateSelection
+        * 
+        * DEPRECATED by 2.0
+        * 
+        * Update color dialog selection to match current selectedColor value.
+        */
+       WCP.ColorPicker.prototype.updateSelection = function() {
+               this.redrawSliders();
+               this.updateSliders();
+               return this;
+       };
+
+
+       /**
+        * Function: updateInput
+        * 
+        * Since 3.0
+        * 
+        * Update input value and background color (if preview is on)
+        */
+       WCP.ColorPicker.prototype.updateInput = function() {
+               // Skip if not yet initialized
+               if(this.widget == null)
+                       return this;
+                       
+               var $input = $(this.input);
+               
+               // #13 only update if value is different to avoid cursor repositioned to the end of text on some browsers
+               if(this.input.value != this.getValue()) {
+                       this.input.value = this.getValue();
+               }
+               
+               if( this.options.preview ) {
+                       $input.css('background', WCP.colorToStr( this.color, 'rgba' ));
+                       if( this.color.v > .5 ) {
+                               $input.css('color', 'black');
+                       }
+                       else {
+                               $input.css('color', 'white');
+                       }
+               }
+       };
+
+
+       /**
+        * Function: updateActiveControl
+        * 
+        * Move the active control.
+        */
+       WCP.ColorPicker.prototype.updateActiveControl = function( e ) {
+               var $control = $( $('body').data('jQWCP.activeControl') ); // Refers to slider wrapper
+               
+               if($control.length == 0)
+                       return;
+               
+               var $input = $(this.input);
+               var options = this.options;
+               var color = this.color;
+               
+               // pageX and pageY wrapper for touches
+               if(e.pageX == undefined && e.originalEvent.touches.length > 0) {
+                       e.pageX = e.originalEvent.touches[0].pageX;
+                       e.pageY = e.originalEvent.touches[0].pageY;
+               }
+               //$('#log').html(e.pageX + '/' + e.pageY);
+               
+               /// WHEEL CONTROL ///
+               if($control.hasClass('jQWCP-wWheel')) {
+                       var $cursor = $control.find('.jQWCP-wWheelCursor');
+                       var $overlay = $control.find('.jQWCP-wWheelOverlay');
+                       
+                       var relX = (e.pageX - $control.offset().left - ($control.width() / 2)) / ($control.width() / 2);
+                       var relY = - (e.pageY - $control.offset().top - ($control.height() / 2)) / ($control.height() / 2);
+                       
+                       // BUG_RELATIVE_PAGE_ORIGIN workaround
+                       if(WCP.BUG_RELATIVE_PAGE_ORIGIN) {
+                               var relX = (e.pageX - ($control.get(0).getBoundingClientRect().left - WCP.ORIGIN.left) - ($control.width() / 2)) / ($control.width() / 2);
+                               var relY = - (e.pageY - ($control.get(0).getBoundingClientRect().top - WCP.ORIGIN.top) - ($control.height() / 2)) / ($control.height() / 2);
+                       }
+                       
+                       //console.log(relX + ' ' + relY);
+                       
+                       // Sat value is calculated from the distance of the cursor from the central point
+                       var sat = Math.sqrt(Math.pow(relX, 2) + Math.pow(relY, 2));
+                       // If distance is out of bound, reset to the upper bound
+                       if(sat > 1) {
+                               sat = 1;
+                       }
+                       
+                       // Snap to 0,0
+                       if(options.snap && sat < options.snapTolerance) {
+                               sat = 0;
+                       }
+                       
+                       // Hue is calculated from the angle of the cursor. 0deg is set to the right, and increase counter-clockwise.
+                       var hue = (relX == 0 && relY == 0) ? 0 : Math.atan( relY / relX ) / ( 2 * Math.PI );
+                       // If hue is negative, then fix the angle value (meaning angle is in either Q2 or Q4)
+                       if( hue < 0 ) {
+                               hue += 0.5;
+                       }
+                       // If y is negative, then fix the angle value (meaning angle is in either Q3 or Q4)
+                       if( relY < 0 ) {
+                               hue += 0.5;
+                       }
+                       
+                       this.setHsv(hue, sat, color.v);
+               }
+               
+               /// SLIDER CONTROL ///
+               else if($control.hasClass('jQWCP-slider-wrapper')) {
+                       var $cursor = $control.find('.jQWCP-scursor');
+                       
+                       var relY = (e.pageY - $control.offset().top) / $control.height();
+                       
+                       // BUG_RELATIVE_PAGE_ORIGIN workaround
+                       if(WCP.BUG_RELATIVE_PAGE_ORIGIN) {
+                               var relY = (e.pageY - ($control.get(0).getBoundingClientRect().top - WCP.ORIGIN.top)) / $control.height();
+                       }
+                       
+                       var value = relY < 0 ? 0 : relY > 1 ? 1 : relY;
+                       
+                       // Snap to 0.0, 0.5, and 1.0
+                       //console.log(value);
+                       if(options.snap && value < options.snapTolerance) {
+                               value = 0;
+                       }
+                       else if(options.snap && value > 1-options.snapTolerance) {
+                               value = 1;
+                       }
+                       if(options.snap && value > 0.5-options.snapTolerance && value < 0.5+options.snapTolerance) {
+                               value = 0.5;
+                       }
+                       
+                       $cursor.css('top', (value * $control.height()) + 'px');
+                       
+                       /// Update color value ///
+                       // Red
+                       if($control.hasClass('jQWCP-wRed')) {
+                               this.setRgb(1-value, color.g, color.b);
+                       }
+                       // Green
+                       if($control.hasClass('jQWCP-wGreen')) {
+                               this.setRgb(color.r, 1-value, color.b);
+                       }
+                       // Blue
+                       if($control.hasClass('jQWCP-wBlue')) {
+                               this.setRgb(color.r, color.g, 1-value);
+                       }
+                       // Hue
+                       if($control.hasClass('jQWCP-wHue')) {
+                               this.setHsv(value, color.s, color.v);
+                       }
+                       // Saturation
+                       if($control.hasClass('jQWCP-wSat')) {
+                               this.setHsv(color.h, 1-value, color.v);
+                       }
+                       // Value
+                       if($control.hasClass('jQWCP-wVal')) {
+                               this.setHsv(color.h, color.s, 1-value);
+                       }
+                       // Alpha
+                       if($control.hasClass('jQWCP-wAlpha')) {
+                               this.setAlpha(1-value);
+                       }
+               }
+       };
+
+
+       /**
+        * Function: getColor
+        * 
+        * Since 2.0
+        * 
+        * Return color components as an object. The object consists of:
+        * { 
+        *   r: red
+        *   g: green
+        *   b: blue
+        *   h: hue
+        *   s: saturation
+        *   v: value
+        *   a: alpha
+        * }
+        */
+       WCP.ColorPicker.prototype.getColor = function() {
+               return this.color;
+       };
+
+
+       /**
+        * Function: getValue
+        * 
+        * Get the color value as string.
+        */
+       WCP.ColorPicker.prototype.getValue = function( format ) {
+               var options = this.options;
+               
+               if( format == null ) {
+                       format = options.format;
+               }
+                       
+               // If settings.rounding is TRUE, round alpha value to N decimal digits
+               if(options.rounding >= 0) {
+                       this.color.a = Math.round(this.color.a * Math.pow(10, options.rounding)) / Math.pow(10, options.rounding);
+               }
+               return WCP.colorToStr( this.color, format );
+       };
+
+
+       /**
+        * Function: setValue
+        * 
+        * Set the color value as string.
+        * 
+        * Parameters:
+        *   value       - <string> String representation of color.
+        *   updateInput - <boolean> Whether to update input text. Default is true.
+        */
+       WCP.ColorPicker.prototype.setValue = function( value, updateInput ) {
+               var color = WCP.strToColor(value);
+               if(!color)
+                       return this;
+                       
+               return this.setColor(color, updateInput);
+       }
+
+
+       /**
+        * Function: setColor
+        * 
+        * Introduced in 2.0
+        * 
+        * Set color by passing an object consisting of:
+        * { r, g, b, a } or
+        * { h, s, v, a }
+        * 
+        * For consistency with options.color value, this function also 
+        * accepts string value.
+        * 
+        * Parameters:
+        *   color       - <object> Color object or string representation of color.
+        *   updateInput - <boolean> Whether to update input text. Default is true.
+        */
+       WCP.ColorPicker.prototype.setColor = function( color, updateInput ) {
+               if(typeof color === "string") {
+                       return this.setValue(color, updateInput);
+               }
+               else if(color.r != null) {
+                       return this.setRgba(color.r, color.g, color.b, color.a, updateInput);
+               }
+               else if(color.h != null) {
+                       return this.setHsva(color.h, color.s, color.v, color.a, updateInput);
+               }
+               else if(color.a != null) {
+                       return this.setAlpha(color.a, updateInput);
+               }
+               return this;
+       };
+
+
+       /**
+        * Function: setRgba
+        * 
+        * Introduced in 2.0
+        * 
+        * Set color using RGBA combination.
+        * 
+        * Parameters:
+        *   r           - <number> Red component [0..1]
+        *   g           - <number> Green component [0..1]
+        *   b           - <number> Blue component [0..1]
+        *   a           - <number> Alpha value [0..1]
+        *   updateInput - <boolean> Whether to update input text. Default is true.
+        */
+       WCP.ColorPicker.prototype.setRgba = function( r, g, b, a, updateInput ) {
+               // Default value
+               if(updateInput === undefined) updateInput = true;
+               
+               var color = this.color;
+               color.r = r;
+               color.g = g;
+               color.b = b;
+               
+               if(a != null) {
+                       color.a = a;
+               }
+               
+               var hsv = WCP.rgbToHsv(r, g, b);
+               color.h = hsv.h;
+               color.s = hsv.s;
+               color.v = hsv.v;
+
+               this.updateSliders();
+               this.redrawSliders();
+               if(updateInput) {
+                       this.updateInput();
+               }
+               return this;
+       };
+
+
+       /**
+        * Function: setRgb
+        * 
+        * Introduced in 2.0
+        * 
+        * Set color using RGB combination.
+        * 
+        * Parameters:
+        *   r           - <number> Red component [0..1]
+        *   g           - <number> Green component [0..1]
+        *   b           - <number> Blue component [0..1]
+        *   updateInput - <boolean> Whether to update input text.
+        */
+       WCP.ColorPicker.prototype.setRgb = function( r, g, b, updateInput ) {
+               return this.setRgba(r, g, b, null, updateInput);
+       };
+
+
+       /**
+        * Function: setHsva
+        * 
+        * Introduced in 2.0
+        * 
+        * Set color using HSVA combination.
+        * 
+        * Parameters:
+        *   h           - <number> Hue component [0..1]
+        *   s           - <number> Saturation component [0..1]
+        *   v           - <number> Value component [0..1]
+        *   a           - <number> Alpha value [0..1]
+        *   updateInput - <boolean> Whether to update input text. Default is true.
+        */
+       WCP.ColorPicker.prototype.setHsva = function( h, s, v, a, updateInput ) {
+               // Default value
+               if(updateInput === undefined) updateInput = true;
+               
+               var color = this.color;
+               color.h = h;
+               color.s = s;
+               color.v = v;
+               
+               if(a != null) {
+                       color.a = a;
+               }
+               
+               var rgb = WCP.hsvToRgb(h, s, v);
+               color.r = rgb.r;
+               color.g = rgb.g;
+               color.b = rgb.b;
+               
+               this.updateSliders();
+               this.redrawSliders();
+               if(updateInput) {
+                       this.updateInput();
+               }
+               return this;
+       };
+
+
+       /**
+        * Function: setHsv
+        * 
+        * Introduced in 2.0
+        * 
+        * Set color using HSV combination.
+        * 
+        * Parameters:
+        *   h           - <number> Hue component [0..1]
+        *   s           - <number> Saturation component [0..1]
+        *   v           - <number> Value component [0..1]
+        *   updateInput - <boolean> Whether to update input text.
+        */
+       WCP.ColorPicker.prototype.setHsv = function( h, s, v, updateInput ) {
+               return this.setHsva(h, s, v, null, updateInput);
+       };
+
+
+       /**
+        * Function: setAlpha
+        * 
+        * Introduced in 2.0
+        * 
+        * Set alpha value.
+        * 
+        * Parameters:
+        *   value       - <number> Alpha value [0..1]
+        *   updateInput - <boolean> Whether to update input text. Default is true.
+        */
+       WCP.ColorPicker.prototype.setAlpha = function( value, updateInput ) {
+               // Default value
+               if(updateInput === undefined) updateInput = true;
+               
+               this.color.a = value;
+               
+               this.updateSliders();
+               this.redrawSliders();
+               if(updateInput) {
+                       this.updateInput();
+               }
+               return this;
+       };
+
+
+       /**
+        * Function: show
+        * 
+        * Show the color picker dialog. This function is only applicable to 
+        * popup mode color picker layout.
+        */
+       WCP.ColorPicker.prototype.show = function() {
+               var input = this.input;
+               var $input = $(input); // Refers to input elm
+               var $widget = $(this.widget);
+               var options = this.options;
+               
+               // Don't do anything if not using popup layout
+               if( options.layout != "popup" )
+                       return;
+                       
+               // Don't do anything if the popup is already shown and attached 
+               // to the correct input elm
+               //if( this == $widget.data('jQWCP.instance') )
+                       //return;
+                       
+               // Attach instance to widget (because popup widget is global)
+               $widget.data('jQWCP.instance', this);
+               
+               // Terminate ongoing transitions
+               $widget.stop( true, true );
+               
+               // Reposition the popup window
+               $widget.css({
+                       top: (input.getBoundingClientRect().top - WCP.ORIGIN.top + $input.outerHeight()) + 'px',
+                       left: (input.getBoundingClientRect().left - WCP.ORIGIN.left) + 'px'
+               });
+               
+               // Refresh widget with this instance's options
+               this.refreshWidget();
+               
+               // Redraw sliders
+               this.redrawSliders();
+               this.updateSliders();
+               
+               // Store last textfield value (to determine whether to trigger onchange event later)
+               this.lastValue = input.value;
+               
+               $widget.fadeIn( options.animDuration );
+               
+               // If hideKeyboard is true, force to hide soft keyboard
+               if(options.hideKeyboard) {
+                       $input.blur();
+                       $(WCP.ColorPicker.overlay).show();
+               }
+        
+        // On mobile layout, autoscroll page to keep input visible
+        if($widget.hasClass('jQWCP-mobile')) {
+            var scrollTop = $('html').scrollTop();
+            var inputTop = input.getBoundingClientRect().top - WCP.ORIGIN.top;
+            
+            // If input is way too top
+            if(inputTop < scrollTop) {
+                $('html').animate({ scrollTop: inputTop});
+            }
+            
+            // If input is way too bottom
+            else if(inputTop + $input.outerHeight() > scrollTop + window.innerHeight - $widget.outerHeight()) {
+                $('html').animate({ scrollTop: inputTop + $input.outerHeight() - window.innerHeight + $widget.outerHeight()});
+            }
+        }
+       };
+
+
+
+       /**
+        * Function: hide
+        * 
+        * Hide the color picker dialog. This function is only applicable to 
+        * popup mode color picker layout.
+        */
+       WCP.ColorPicker.prototype.hide = function() {
+               var $widget = $(this.widget);
+               
+               // Only hide if popup belongs to this instance
+               if(this != $widget.data('jQWCP.instance'))
+                       return;
+               
+               $widget.fadeOut( this.options.animDuration );
+               $(WCP.ColorPicker.overlay).hide();
+       };
+
+
+
+       ////////////////////
+       // Event Handlers //
+       ////////////////////
+
+       WCP.Handler = {};
+
+       /**
+        * input.onFocus.popup
+        */
+       WCP.Handler.input_focus_popup = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               instance.show();
+               
+               // Workaround to prevent on screen keyboard from appearing
+               if($(this).attr('readonly') == null) {
+                       $(this).attr('readonly', true);
+                       setTimeout(function() {
+                               $(instance.input).removeAttr('readonly');
+                       });
+                       
+                       // Firefox on Android
+                       if(navigator.userAgent.match(/Android .* Firefox/) != null) {
+                               setTimeout(function() {
+                                       $(instance.input).attr('readonly', true);
+                                       $(instance.input).one('blur', function() {
+                                               $(instance.input).removeAttr('readonly');
+                                       });
+                               });
+                       }
+               }
+       };
+
+
+       /**
+        * input.onBlur.popup
+        * 
+        * onBlur event handler for popup layout.
+        */
+       WCP.Handler.input_blur_popup = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               
+               // If keyboard is hidden, input is always blurred so 
+               // no point in hiding
+               if(instance.options.hideKeyboard)
+                       return;
+                       
+               instance.hide();
+               
+               // Trigger 'change' event only when it was modified by widget
+               // because user typing on the textfield will automatically
+               // trigger 'change' event on blur.
+               if(instance.lastValue != this.value) {
+                       $(this).trigger('change');
+               }
+       };
+       
+       
+       /**
+        * input.onKeyUp
+        * 
+        * Update the color picker while user type in color value.
+        */
+       WCP.Handler.input_keyup = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var color = WCP.strToColor(this.value);
+               if(color) {
+                       instance.setColor(color, false);
+               }
+       };
+
+
+       /**
+        * input.onChange
+        * 
+        * Reformat the input value based on selected color and configurations.
+        */
+       WCP.Handler.input_change = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var color = WCP.strToColor(this.value);
+               
+               // If autoFormat option is enabled, try to reformat the value if it is a valid color
+               if(instance.options.autoFormat && color) {
+                       instance.setColor(color, true);
+               }
+               
+               // If validate option is enabled, revert the value if it is not a valid color
+               else if(instance.options.validate && !color && this.value != '') {
+                       this.value = instance.getValue();
+               }
+       };
+
+
+       /**
+        * widget.onFocus.block
+        * 
+        * Prepare runtime widget data
+        */
+       WCP.Handler.widget_focus_block = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               // Store last textfield value
+               instance.lastValue = instance.input.value;
+               
+               // Trigger focus event
+               $input.triggerHandler('focus');
+       };
+
+
+       /**
+        * widget.onMouseDown.popup
+        * 
+        * Prevent loss focus of the input causing the dialog to be hidden
+        * because of input blur event.
+        */
+       WCP.Handler.widget_mousedown_popup = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               // Temporarily unbind blur and focus event until mouse is released
+               $input.off('focus.wheelColorPicker');
+               $input.off('blur.wheelColorPicker');
+               
+               // Temporarily unbind all blur events until mouse is released
+               // data('events') is deprecated since jquery 1.8
+               if($input.data('events') != undefined) {
+                       var blurEvents = $input.data('events').blur;
+               }
+               else {
+                       var blurEvents = undefined;
+               }
+               var suspendedEvents = { blur: [] };
+               //suspendedEvents.blur = blurEvents;
+               //$input.off('blur');
+               if(blurEvents != undefined) {
+                       for(var i = 0; i < blurEvents.length; i++) {
+                               suspendedEvents.blur.push(blurEvents[i]);
+                               //suspendedEvents.blur['blur' + (blurEvents[i].namespace != '' ? blurEvents[i].namespace : '')] = blurEvents[i].handler;
+                       }
+               }
+               $input.data('jQWCP.suspendedEvents', suspendedEvents);
+               //console.log(blurEvents);
+               //console.log($input.data('jQWCP.suspendedEvents'));
+       };
+
+       /**
+        * widget.onMouseUp
+        * 
+        * Re-bind events that was unbound by widget_mousedown_popup.
+        */
+       /*WCP.Handler.widget_mouseup_popup = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+                //Input elm must always be focused, unless hideKeyboard is set to true
+               if(!instance.options.hideKeyboard) {
+                       $input.trigger('focus.jQWCP_DONT_TRIGGER_EVENTS'); // This allow input to be focused without triggering events
+               }
+               
+                //Rebind blur & focusevent
+               $input.on('focus.wheelColorPicker', WCP.Handler.input_focus_popup);
+               $input.on('blur.wheelColorPicker', WCP.Handler.input_blur_popup);
+               
+       };*/
+
+
+
+       /**
+        * widget.onBlur
+        * 
+        * Try to trigger onChange event if value has been changed.
+        */
+       WCP.Handler.widget_blur_block = function( e ) {
+               var instance = $(this).data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               // Trigger 'change' event only when it was modified by widget
+               // because user typing on the textfield will automatically
+               // trigger 'change' event on blur.
+               if(instance.lastValue != instance.input.value) {
+                       $input.trigger('change');
+               }
+               
+               // Trigger blur event
+               $input.triggerHandler('blur');
+       };
+
+
+       /**
+        * wheelCursor.onMouseDown
+        * 
+        * Begin clicking the wheel down. This will allow user to move 
+        * the crosshair although the mouse is outside the wheel.
+        */
+       WCP.Handler.wheelCursor_mousedown = function( e ) {
+               var $this = $(this); // Refers to cursor
+               var $widget = $this.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               $('body').data('jQWCP.activeControl', $this.parent().get(0));
+               
+               // Trigger sliderdown event
+               $input.trigger('sliderdown');
+       };
+
+
+       /**
+        * wheel.onMouseDown
+        * 
+        * Begin clicking the wheel down. This will allow user to move 
+        * the crosshair although the mouse is outside the wheel.
+        * 
+        * Basically this is the same as wheelCursor_mousedown handler
+        */
+       WCP.Handler.wheel_mousedown = function( e ) {
+               var $this = $(this); // Refers to wheel
+               var $widget = $this.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               $('body').data('jQWCP.activeControl', $this.get(0));
+               
+               // Trigger sliderdown event
+               $input.trigger('sliderdown');
+       };
+
+
+       /**
+        * slider.onMouseDown
+        * 
+        * Begin clicking the slider down. This will allow user to move 
+        * the slider although the mouse is outside the slider.
+        */
+       WCP.Handler.slider_mousedown = function( e ) {
+               var $this = $(this); // Refers to slider
+               var $widget = $this.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               $('body').data('jQWCP.activeControl', $this.parent().get(0));
+               
+               // Trigger sliderdown event
+               $input.trigger('sliderdown');
+       };
+
+       /**
+        * sliderCursor.onMouseDown
+        * 
+        * Begin clicking the slider down. This will allow user to move 
+        * the slider although the mouse is outside the slider.
+        */
+       WCP.Handler.sliderCursor_mousedown = function( e ) {
+               var $this = $(this); // Refers to slider cursor
+               var $widget = $this.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               $('body').data('jQWCP.activeControl', $this.parent().get(0));
+               
+               // Trigger sliderdown event
+               $input.trigger('sliderdown');
+       };
+
+
+
+       /**
+        * html.onMouseUp
+        * 
+        * Clear active control reference.
+        * Also do cleanups after widget.onMouseDown.popup
+        * 
+        * Note: This event handler is also applied to touchend
+        */
+       WCP.Handler.html_mouseup = function( e ) {
+               var $control = $( $('body').data('jQWCP.activeControl') ); // Refers to slider wrapper or wheel
+               
+               // Do stuffs when there's active control
+               if($control.length == 0)
+                       return;
+                       
+               var $widget = $control.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               
+               // Rebind blur and focus event to input elm which was 
+               // temporarily released when popup dialog is shown
+               if(instance.options.layout == 'popup') {
+                       // Focus first before binding event so it wont get fired
+                       // Input elm must always be focused, unless hideKeyboard is set to true
+                       if(!instance.options.hideKeyboard) {
+                               $input.trigger('focus.jQWCP_DONT_TRIGGER_EVENTS'); // This allow input to be focused without triggering events
+                       }
+                       
+                       // Rebind blur & focusevent
+                       $input.on('focus.wheelColorPicker', WCP.Handler.input_focus_popup);
+                       $input.on('blur.wheelColorPicker', WCP.Handler.input_blur_popup);
+                       
+                       // Rebind suspended events
+                       var suspendedEvents = $input.data('jQWCP.suspendedEvents');
+                       if(suspendedEvents != undefined) {
+                               var blurEvents = suspendedEvents.blur;
+                               for(var i = 0; i < blurEvents.length; i++) {
+                                       $input.on('blur' + (blurEvents[i].namespace == '' ? '' : '.' + blurEvents[i].namespace), blurEvents[i].handler);
+                               }
+                       }
+               }
+               
+               
+               // Update active control
+               if($control.length != 0) {
+                       // Last time update active control before clearing
+                       // Only call this function if mouse position is known
+                       // On touch action, touch point is not available
+                       if(e.pageX != undefined) {
+                               instance.updateActiveControl( e );
+                       }
+                       
+                       // Clear active control reference
+                       $('body').data('jQWCP.activeControl', null);
+                       
+                       // Trigger sliderup event
+                       $input.trigger('sliderup');
+               }
+       };
+
+
+       /**
+        * html.onMouseMove
+        * 
+        * Move the active slider (when mouse click is down).
+        * 
+        * Note: This event handler is also applied to touchmove
+        */
+       WCP.Handler.html_mousemove = function( e ) {
+               var $control = $( $('body').data('jQWCP.activeControl') ); // Refers to slider wrapper or wheel
+               
+               // Do stuffs when there's active control
+               if($control.length == 0)
+                       return;
+                       
+               // If active, prevent default
+               e.preventDefault();
+               
+               var $widget = $control.closest('.jQWCP-wWidget');
+               var instance = $widget.data('jQWCP.instance');
+               var $input = $(instance.input);
+               
+               instance.updateActiveControl( e );
+               
+               // Trigger slidermove event
+               $input.trigger('slidermove');
+               
+               return false;
+       };
+
+
+       /**
+        * window.onResize
+        * 
+        * Adjust block widgets
+        */
+       WCP.Handler.window_resize = function( e ) {
+        var $widgets = $('body .jQWCP-wWidget.jQWCP-block');
+        
+        $widgets.each(function() {
+            var instance = $(this).data('jQWCP.instance');
+            instance.refreshWidget();
+            instance.redrawSliders();
+        });
+       };
+
+
+       /**
+        * overlay.onClick
+        * 
+        * Hide colorpicker popup dialog if overlay is clicked.
+        * This has the same effect as blurring input element if hideKeyboard = false.
+        */
+       WCP.Handler.overlay_click = function( e ) {
+               if(WCP.ColorPicker.widget == null)
+                       return;
+               
+               var $widget = $(WCP.ColorPicker.widget);
+               var instance = $widget.data('jQWCP.instance');
+               
+               // If no instance set, do nothing
+               if(instance != null) {
+                       var $input = $(instance.input);
+                       
+                       // Trigger 'change' event only when it was modified by widget
+                       // because user typing on the textfield will automatically
+                       // trigger 'change' event on blur.
+                       if(instance.lastValue != instance.input.value) {
+                               $input.trigger('change');
+                       }
+                       
+                       instance.hide();
+               }
+       };
+
+       /******************************************************************/
+
+       ////////////////////////////////////////////////////////
+       // Automatically initialize color picker on page load //
+       // for elements with data-wheelcolorpicker attribute. //
+       ////////////////////////////////////////////////////////
+
+       $(document).ready(function() {
+               $('[data-wheelcolorpicker]').wheelColorPicker({ htmlOptions: true });
+       });
+
+
+
+       /******************************************************************/
+
+       //////////////////////////////////
+       // Browser specific workarounds //
+       //////////////////////////////////
+
+       (function() {
+               // MOZILLA //
+               
+               // Force low resolution slider canvases to improve performance
+               // Note: Do not rely on $.browser since it's obsolete by jQuery 2.x
+               if($.browser != undefined && $.browser.mozilla) {
+                       $.fn.wheelColorPicker.defaults.quality = 0.2;
+               }
+               
+               // MOBILE CHROME //
+               
+               // BUG_RELATIVE_PAGE_ORIGIN
+               // Calibrate the coordinate of top left point of the page
+               // On mobile chrome, the top left of the page is not always set at (0,0)
+               // making window.scrollX/Y and $.offset() useless
+               $(document).ready(function() {
+                       $('body').append(
+                               '<div id="jQWCP-PageOrigin" style="position: absolute; top: 0; left: 0; height: 0; width: 0;"></div>'
+                       );
+                       
+                       var origin = document.getElementById('jQWCP-PageOrigin').getBoundingClientRect();
+                       WCP.ORIGIN = origin;
+               
+                       $(window).on('scroll.jQWCP_RelativePageOriginBugFix', function() {
+                               var origin = document.getElementById('jQWCP-PageOrigin').getBoundingClientRect();
+                               WCP.ORIGIN = origin;
+                               if(origin.left != 0 || origin.top != 0) {
+                                       WCP.BUG_RELATIVE_PAGE_ORIGIN = true;
+                               }
+                       });
+               });
+               
+       })();
+
+}) (jQuery);
index 3a487e4..a573709 100644 (file)
@@ -334,6 +334,21 @@ class GlmMembersAdmin_management_events extends GlmDataEventsManagement
 
             default:
 
+                wp_enqueue_script(
+                    'wheel-color-picker',
+                    GLM_MEMBERS_EVENTS_PLUGIN_URL . '/js/jquery.wheelcolorpicker.js',
+                    array( 'jquery' ),
+                    '1.0.0',
+                    false
+                );
+                wp_enqueue_style(
+                    'wheel-color-picker-style',
+                    GLM_MEMBERS_EVENTS_PLUGIN_URL . '/css/wheelcolorpicker.css',
+                    array(),
+                    '1.0.0',
+                    false
+                );
+
                 // Make sure option is set if default
                 $option = 'settings';
 
diff --git a/setup/databaseScripts/create_database_V0.1.7.sql b/setup/databaseScripts/create_database_V0.1.7.sql
deleted file mode 100644 (file)
index 4e25ecb..0000000
+++ /dev/null
@@ -1,280 +0,0 @@
--- Gaslight Media Members Database - Events Add-On
--- File Created: 08/18/17
--- Database Version: 0.1.7
--- Database Creation Script
---
--- This file is called to create a new set of tables for this
--- add-on for the most recent database version for this add-on.
---
--- There should only be one such file in this directory
---
--- To permit each query below to be executed separately,
--- all queries must be separated by a line with four dashes
-
-
--- Amenities
-CREATE TABLE {prefix}amenities (
-    id INT NOT NULL AUTO_INCREMENT,
-    active TINYINT(1) NULL,                   -- Amenity is active flag
-    name TINYTEXT NULL,                       -- Name of amenity
-    descr TEXT NULL,                          -- Description of amenity
-    short_descr TINYTEXT NULL,                -- Short description of amenity
-    uses_value BOOLEAN NULL,                  -- Flag indicating whether the amenity requires a quantity number
-    PRIMARY KEY (id),
-    INDEX(name(20))
-);
-
-----
-
--- Amenity Reference - Links a specific amenity to a specific entity of type ref_type
-CREATE TABLE {prefix}amenity_event (
-    id INT NOT NULL AUTO_INCREMENT,
-    amenity INT NULL,
-    event INT NULL,
-    PRIMARY KEY (id),
-    INDEX(event)
-);
-
-----
-
--- groups
-CREATE TABLE {prefix}amenity_groups (
-    id INT NOT NULL AUTO_INCREMENT,
-    name TINYTEXT NULL,                     -- Name of the Group
-    PRIMARY KEY (id),
-    INDEX(name(20))
-);
-
-----
-
--- Amenity Group - Links a specific amenity to groups
-CREATE TABLE {prefix}grouped_amenities (
-    id INT NOT NULL AUTO_INCREMENT,
-    group_id INT,                              -- Pointer to the group
-    amenity_id INT,                            -- Pointer to the Amenity
-    searchable BOOLEAN DEFAULT '0',         -- Flag indicating whether the amenity group will show in the search form
-    PRIMARY KEY (id)
-);
-
-----
-
--- Categories - Categories for events
-CREATE TABLE {prefix}categories (
-    id INT NOT NULL AUTO_INCREMENT,
-    name TINYTEXT NULL,                                   -- Name of event category
-    descr TINYTEXT NULL,                                  -- Description of this category
-    parent INT NULL,                                      -- Parent category, null or 0 if this is a top level category
-    PRIMARY KEY (id),
-    INDEX(parent)
-);
-
-----
-
--- Event-Category - Categories for specific event records
-CREATE TABLE {prefix}event_categories (
-    id INT NOT NULL AUTO_INCREMENT,
-    event INT NULL,                                       -- Pointer to the event
-    category INT NULL,                                    -- Pointer to the category
-    PRIMARY KEY (id),
-    INDEX(event),
-    INDEX(category)
-);
-
-----
-
--- Event Recurrence - Defines how an event recurs
-CREATE TABLE {prefix}recurrences (
-    id INT NOT NULL AUTO_INCREMENT,
-    event INTEGER NULL,                                   -- Pointer to event
-    name TINYTEXT NULL,                                   -- Name of this recurrence schedule - used on admin calendar
-    start_time TIME NULL,                                 -- Start time of day for event
-    start_time_only BOOLEAN NULL,                         -- Use end of first occurrence flag
-    end_time TIME NULL,                                   -- End time of day for event - If less than start time, assume a date boundry
-    all_day BOOLEAN NULL,                                 -- Flag indicating if this is an all-day event (informational only)
-    start_date DATE NULL,                                 -- Starting Date (if all_day is selected) Used instead of start_time
-    from_date DATE NULL,                                  -- From Date for recurrences
-    to_date DATE NULL,                                    -- To Date for recurrences
-    recurring BOOLEAN NULL,                               -- Flag indicating that event recurs on a schedule rather than all dates
-    month_of_year SMALLINT UNSIGNED NULL,                 -- Month of year (bitmap)
-    week_of_month TINYINT UNSIGNED NULL,                  -- Week of the month (bitmap)
-    day_of_week TINYINT UNSIGNED NULL,                    -- Day of the week (bitmap)
-    by_day_of_month BOOLEAN NULL,                         -- Flag indicating if selecting by days of the month
-    day_of_month INTEGER UNSIGNED NULL,                   -- Day of the month (bitmap)
-    last_day_of_month BOOLEAN NULL,                       -- Last day of the month
-    specific_dates TEXT NULL,                             -- Serialized array of specific dates added to the recurrence
-    holiday INT NULL,                                     -- Pointer to holidays list (for future development)
-    holiday_offset TINYINT,                               -- Offset from holiday (from -128 to +127 days)
-    PRIMARY KEY (id),
-    INDEX(event)
-);
-
-----
-
--- Times - List of actual event times for single and recurring events
-CREATE TABLE {prefix}times (
-    id INT NOT NULL AUTO_INCREMENT,
-    event INT NULL,                                       -- Pointer to the primary record for the event
-    custom_event INT NULL,                                -- Pointer to a customized copy of the event record (if set)
-    recur_id INT NULL,                                    -- Pointer to recurrence entry
-    active BOOLEAN NULL,                                  -- Active flag - normally set but used to temporarily disable a specific date
-    start_time DATETIME NULL,                             -- Date and time event starts
-    end_time DATETIME NULL,                               -- Date and time event ends
-    all_day BOOLEAN NULL,                                 -- All Day flag
-    PRIMARY KEY (id),
-    INDEX(event),
-    INDEX(start_time),
-    INDEX(end_time)
-);
-
-----
-
--- Locations - Locations for event - If there's no location pointing to an event try to use the referenced entity in events table
-CREATE TABLE {prefix}locations (
-    id INT NOT NULL AUTO_INCREMENT,
-    event INT NULL,                                       -- Pointer to the primary or custom event record
-    name TINYTEXT NULL,                                   -- Name of location
-    address TINYTEXT NULL,                                -- Street Address
-    city INT NULL,                                        -- Pointer to city - references main plugin city table
-    state TINYTEXT NULL,                                  -- Two character state abbreviation
-    zip TINYTEXT NULL,                                    -- ZIP/Postal code
-    country TINYTEXT NULL,                                -- Country Code
-    lat FLOAT NULL,                                       -- Latitude of location
-    lon FLOAT NULL,                                       -- Longitude of location
-    region INT NULL,                                      -- Pointer to Region - references main plugin region table
-    phone TINYTEXT NULL,                                  -- Location Phone #
-    url TINYTEXT NULL,                                    -- Location URL
-    email TINYTEXT NULL,                                  -- Location E-Mail Address
-    contact_addon_id INT NULL,                            -- ID of Contact from contact add-on (optional and if available)
-    contact_fname TINYTEXT NULL,                          -- Contact first name for this location (optional)
-    contact_lname TINYTEXT NULL,                          -- Contact last name for this location (optional)
-    contact_phone TINYTEXT NULL,                          -- Contact phone for this location (optional)
-    contact_email TINYTEXT NULL,                          -- Contact E-Mail address (optional)
-    PRIMARY KEY (id)
-);
-
-----
-
--- Events - Base event information - May also be entries here referenced by the "times" table for a custom date.
-CREATE TABLE {prefix}events (
-    id INT NOT NULL AUTO_INCREMENT,
-    status INT NULL,                                      -- Status for this event, see config['status']
-    custom_time INT NULL,                                 -- If this is a custom record for a specific instance (date) this points to that times table entry
-    root_event INT NULL,                                  -- Root event pointer if this is a custom record for a specific instance (date) (if custom_time is set)
-    created DATETIME NULL,                                -- Date/Time event was created or date custom event record was created if custom record
-    updated DATETIME NULL,                                -- Date/Time this event record was last updated
-    approved DATETIME NULL,                               -- Date/Ttime this event record was approved
-    ref_type INT NULL,                                    -- Type of entity this event is associated with - See config['ref_type']
-    ref_dest INT NULL,                                    -- Pointer to the specific entity of ref_type this event is associated with
-    hide_address BOOLEAN NULL,                            -- Option to hide address on front-end
-    featured BOOLEAN NULL,                                -- Option to mark as featured event
-    slideshow BOOLEAN NULL,                               -- Option to mark for use in slide show
-    major BOOLEAN NULL,                                   -- Option to mark as a major event
-    name TINYTEXT NULL,                                   -- Name of this event
-    name_slug TINYTEXT NULL,                              -- Slug for this event
-    header TINYTEXT NULL,                                 -- Header text for front-end display - NOT CURRENTLY USED
-    intro TINYTEXT NULL,                                  -- Intro text for front-end display
-    descr TEXT NULL,                                      -- Full description text
-    image TINYTEXT NULL,                                  -- Image file name
-    file1 TINYTEXT NULL,                                  -- File name for a single uploaded file #1
-    file1_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #1
-    file2 TINYTEXT NULL,                                  -- File name for a single uploaded file #2
-    file2_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #2
-    file3 TINYTEXT NULL,                                  -- File name for a single uploaded file #3
-    file3_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #3
-    url TINYTEXT NULL,                                    -- Event URL
-    ticket_url TINYTEXT NULL,                             -- Ticket URL
-    registration_url TINYTEXT NULL,                       -- Registration URL
-    cost TINYTEXT NULL,                                   -- Description of event cost
-    admin_ref_type INT NULL,                              -- Type of admin contact if using a member contact
-    admin_ref_dest INT NULL,                              -- Pointer to admin contact record if using a member contact
-    admin_name TINYTEXT NULL,                             -- Admin Contact Name if not using a member contact
-    admin_org TINYTEXT NULL,                              -- Admin Contact Organization if not using a member contact
-    admin_email TINYTEXT NULL,                            -- Admin Contact E-Mail if not using a member contact
-    admin_phone TINYTEXT NULL,                            -- Admin Contact Phone if not using a member contact
-    free BOOLEAN NULL,                                    -- Event is Free
-    contact_email TINYTEXT NULL,                          -- Contact E-mail address
-    contact_name TINYTEXT NULL,                           -- Contact name
-    contact_phone TINYTEXT NULL,                          -- Event Phone
-    use_member_location BOOLEAN NULL,                     -- Use location of the member (if provided) rather than location table data
-    other_ref_dest INT NULL,                              -- Set location of the event to another member
-    old_event_id INT NULL,                                -- ID of event from old site for reference
-    ical_uid TINYTEXT NULL,                               -- The ical UID for this event.
-    notes TEXT NULL,                                      -- Internal notes for this event
-    PRIMARY KEY (id),
-    INDEX(custom_time),
-    INDEX(root_event),
-    INDEX(ref_type),
-    INDEX(ref_dest),
-    INDEX(featured),
-    INDEX(slideshow),
-    INDEX(major)
-);
-
-----
-
--- Event Management Settings
-CREATE TABLE {prefix}management (
-    id INT NOT NULL AUTO_INCREMENT,
-    canonical_event_page TINYTEXT NULL,                   -- Canonical page slug for event detail
-    pdf_logo TINYTEXT NULL,                               -- Image for the Top of the PDF
-    footer_text TINYTEXT NULL,                            -- Image for the Top of the PDF
-    to_email TINYTEXT NULL,                               -- Email address of the recipient
-    from_email TINYTEXT NULL,                             -- Email address of the sender
-    email_notification TEXT NULL,                         -- Email notification message
-    calendar_view TINYTEXT NULL,                          -- Default calendar view
-    event_default_state TINYTEXT NULL,                    -- Default calendar view
-    term_event_amenities_singular TINYTEXT NULL,          -- Singular term to use for event amenities
-    term_event_amenities_plural TINYTEXT NULL,            -- Plural term to use for event amenities
-    use_event_amenities BOOLEAN NULL,                     -- Whether amenities are used at all for events
-    use_venue_locations BOOLEAN NULL,                     -- To use Other members as the location of event
-    member_events_allowed BOOLEAN NULL,                   -- If members are allowed to add events.
-    member_events_moderated BOOLEAN DEFAULT '0',          -- If member edits are moderated.
-    ical_feed_image_size TINYTEXT NULL,                   -- Image size to use in iCal Feed
-    event_display_member_message BOOLEAN DEFAULT '0',     -- Boolean to show member message or not
-    event_member_message TEXT NULL,                       -- Member Message
-    PRIMARY KEY (id)
-);
-
-----
-
--- Set default event management entry
-INSERT INTO {prefix}management
-    ( id, canonical_event_page, term_event_amenities_singular, term_event_amenities_plural, member_events_allowed, ical_feed_image_size, event_display_member_message, event_member_message )
-   VALUES
-    ( 1, 'event-detail', 'Amenity', 'Amenities', 1, 'large', 0, '')
-;
-
-----
-
--- Event iCal Feed imports
-CREATE TABLE {prefix}feed_import (
-    id INT NOT NULL AUTO_INCREMENT,
-    feed_url TEXT NOT NULL,            -- The ical feed url to import
-    created DATETIME NULL,             -- The date this feed was created
-    updated DATETIME NULL,             -- Last time this feed was updated
-    duration INT NULL,                 -- The time it took to fetch the feed
-    events INT NULL,                   -- The number of events last fetched
-    PRIMARY KEY (id)
-);
-
-----
-
--- Event Email Notifications
-CREATE TABLE {prefix}email_notifications (
-    id INT NOT NULL AUTO_INCREMENT,
-    declined_message TEXT NULL,    -- Event declined message
-    approved_message TEXT NULL,    -- Event approved message
-    to_email TINYTEXT NULL,        -- To Email Address
-    from_email TINYTEXT NULL,      -- From Email Address
-    email_notification TEXT NULL,  -- Email notification message
-    PRIMARY KEY (id)
-);
-
-----
-
--- Set default event email settings
-INSERT INTO {prefix}email_notifications
-    ( id, declined_message, approved_message )
-   VALUES
-    ( 1, 'The Event parameters do not comply with our event guidelines.', 'Your event has been approved.' )
-;
diff --git a/setup/databaseScripts/create_database_V0.1.8.sql b/setup/databaseScripts/create_database_V0.1.8.sql
new file mode 100644 (file)
index 0000000..ec852b8
--- /dev/null
@@ -0,0 +1,288 @@
+-- Gaslight Media Members Database - Events Add-On
+-- File Created: 08/18/17
+-- Database Version: 0.1.7
+-- Database Creation Script
+--
+-- This file is called to create a new set of tables for this
+-- add-on for the most recent database version for this add-on.
+--
+-- There should only be one such file in this directory
+--
+-- To permit each query below to be executed separately,
+-- all queries must be separated by a line with four dashes
+
+
+-- Amenities
+CREATE TABLE {prefix}amenities (
+    id INT NOT NULL AUTO_INCREMENT,
+    active TINYINT(1) NULL,                   -- Amenity is active flag
+    name TINYTEXT NULL,                       -- Name of amenity
+    descr TEXT NULL,                          -- Description of amenity
+    short_descr TINYTEXT NULL,                -- Short description of amenity
+    uses_value BOOLEAN NULL,                  -- Flag indicating whether the amenity requires a quantity number
+    PRIMARY KEY (id),
+    INDEX(name(20))
+);
+
+----
+
+-- Amenity Reference - Links a specific amenity to a specific entity of type ref_type
+CREATE TABLE {prefix}amenity_event (
+    id INT NOT NULL AUTO_INCREMENT,
+    amenity INT NULL,
+    event INT NULL,
+    PRIMARY KEY (id),
+    INDEX(event)
+);
+
+----
+
+-- groups
+CREATE TABLE {prefix}amenity_groups (
+    id INT NOT NULL AUTO_INCREMENT,
+    name TINYTEXT NULL,                     -- Name of the Group
+    PRIMARY KEY (id),
+    INDEX(name(20))
+);
+
+----
+
+-- Amenity Group - Links a specific amenity to groups
+CREATE TABLE {prefix}grouped_amenities (
+    id INT NOT NULL AUTO_INCREMENT,
+    group_id INT,                              -- Pointer to the group
+    amenity_id INT,                            -- Pointer to the Amenity
+    searchable BOOLEAN DEFAULT '0',         -- Flag indicating whether the amenity group will show in the search form
+    PRIMARY KEY (id)
+);
+
+----
+
+-- Categories - Categories for events
+CREATE TABLE {prefix}categories (
+    id INT NOT NULL AUTO_INCREMENT,
+    name TINYTEXT NULL,                                   -- Name of event category
+    descr TINYTEXT NULL,                                  -- Description of this category
+    parent INT NULL,                                      -- Parent category, null or 0 if this is a top level category
+    PRIMARY KEY (id),
+    INDEX(parent)
+);
+
+----
+
+-- Event-Category - Categories for specific event records
+CREATE TABLE {prefix}event_categories (
+    id INT NOT NULL AUTO_INCREMENT,
+    event INT NULL,                                       -- Pointer to the event
+    category INT NULL,                                    -- Pointer to the category
+    PRIMARY KEY (id),
+    INDEX(event),
+    INDEX(category)
+);
+
+----
+
+-- Event Recurrence - Defines how an event recurs
+CREATE TABLE {prefix}recurrences (
+    id INT NOT NULL AUTO_INCREMENT,
+    event INTEGER NULL,                                   -- Pointer to event
+    name TINYTEXT NULL,                                   -- Name of this recurrence schedule - used on admin calendar
+    start_time TIME NULL,                                 -- Start time of day for event
+    start_time_only BOOLEAN NULL,                         -- Use end of first occurrence flag
+    end_time TIME NULL,                                   -- End time of day for event - If less than start time, assume a date boundry
+    all_day BOOLEAN NULL,                                 -- Flag indicating if this is an all-day event (informational only)
+    start_date DATE NULL,                                 -- Starting Date (if all_day is selected) Used instead of start_time
+    from_date DATE NULL,                                  -- From Date for recurrences
+    to_date DATE NULL,                                    -- To Date for recurrences
+    recurring BOOLEAN NULL,                               -- Flag indicating that event recurs on a schedule rather than all dates
+    month_of_year SMALLINT UNSIGNED NULL,                 -- Month of year (bitmap)
+    week_of_month TINYINT UNSIGNED NULL,                  -- Week of the month (bitmap)
+    day_of_week TINYINT UNSIGNED NULL,                    -- Day of the week (bitmap)
+    by_day_of_month BOOLEAN NULL,                         -- Flag indicating if selecting by days of the month
+    day_of_month INTEGER UNSIGNED NULL,                   -- Day of the month (bitmap)
+    last_day_of_month BOOLEAN NULL,                       -- Last day of the month
+    specific_dates TEXT NULL,                             -- Serialized array of specific dates added to the recurrence
+    holiday INT NULL,                                     -- Pointer to holidays list (for future development)
+    holiday_offset TINYINT,                               -- Offset from holiday (from -128 to +127 days)
+    PRIMARY KEY (id),
+    INDEX(event)
+);
+
+----
+
+-- Times - List of actual event times for single and recurring events
+CREATE TABLE {prefix}times (
+    id INT NOT NULL AUTO_INCREMENT,
+    event INT NULL,                                       -- Pointer to the primary record for the event
+    custom_event INT NULL,                                -- Pointer to a customized copy of the event record (if set)
+    recur_id INT NULL,                                    -- Pointer to recurrence entry
+    active BOOLEAN NULL,                                  -- Active flag - normally set but used to temporarily disable a specific date
+    start_time DATETIME NULL,                             -- Date and time event starts
+    end_time DATETIME NULL,                               -- Date and time event ends
+    all_day BOOLEAN NULL,                                 -- All Day flag
+    PRIMARY KEY (id),
+    INDEX(event),
+    INDEX(start_time),
+    INDEX(end_time)
+);
+
+----
+
+-- Locations - Locations for event - If there's no location pointing to an event try to use the referenced entity in events table
+CREATE TABLE {prefix}locations (
+    id INT NOT NULL AUTO_INCREMENT,
+    event INT NULL,                                       -- Pointer to the primary or custom event record
+    name TINYTEXT NULL,                                   -- Name of location
+    address TINYTEXT NULL,                                -- Street Address
+    city INT NULL,                                        -- Pointer to city - references main plugin city table
+    state TINYTEXT NULL,                                  -- Two character state abbreviation
+    zip TINYTEXT NULL,                                    -- ZIP/Postal code
+    country TINYTEXT NULL,                                -- Country Code
+    lat FLOAT NULL,                                       -- Latitude of location
+    lon FLOAT NULL,                                       -- Longitude of location
+    region INT NULL,                                      -- Pointer to Region - references main plugin region table
+    phone TINYTEXT NULL,                                  -- Location Phone #
+    url TINYTEXT NULL,                                    -- Location URL
+    email TINYTEXT NULL,                                  -- Location E-Mail Address
+    contact_addon_id INT NULL,                            -- ID of Contact from contact add-on (optional and if available)
+    contact_fname TINYTEXT NULL,                          -- Contact first name for this location (optional)
+    contact_lname TINYTEXT NULL,                          -- Contact last name for this location (optional)
+    contact_phone TINYTEXT NULL,                          -- Contact phone for this location (optional)
+    contact_email TINYTEXT NULL,                          -- Contact E-Mail address (optional)
+    PRIMARY KEY (id)
+);
+
+----
+
+-- Events - Base event information - May also be entries here referenced by the "times" table for a custom date.
+CREATE TABLE {prefix}events (
+    id INT NOT NULL AUTO_INCREMENT,
+    status INT NULL,                                      -- Status for this event, see config['status']
+    custom_time INT NULL,                                 -- If this is a custom record for a specific instance (date) this points to that times table entry
+    root_event INT NULL,                                  -- Root event pointer if this is a custom record for a specific instance (date) (if custom_time is set)
+    created DATETIME NULL,                                -- Date/Time event was created or date custom event record was created if custom record
+    updated DATETIME NULL,                                -- Date/Time this event record was last updated
+    approved DATETIME NULL,                               -- Date/Ttime this event record was approved
+    ref_type INT NULL,                                    -- Type of entity this event is associated with - See config['ref_type']
+    ref_dest INT NULL,                                    -- Pointer to the specific entity of ref_type this event is associated with
+    hide_address BOOLEAN NULL,                            -- Option to hide address on front-end
+    featured BOOLEAN NULL,                                -- Option to mark as featured event
+    slideshow BOOLEAN NULL,                               -- Option to mark for use in slide show
+    major BOOLEAN NULL,                                   -- Option to mark as a major event
+    name TINYTEXT NULL,                                   -- Name of this event
+    name_slug TINYTEXT NULL,                              -- Slug for this event
+    header TINYTEXT NULL,                                 -- Header text for front-end display - NOT CURRENTLY USED
+    intro TINYTEXT NULL,                                  -- Intro text for front-end display
+    descr TEXT NULL,                                      -- Full description text
+    image TINYTEXT NULL,                                  -- Image file name
+    file1 TINYTEXT NULL,                                  -- File name for a single uploaded file #1
+    file1_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #1
+    file2 TINYTEXT NULL,                                  -- File name for a single uploaded file #2
+    file2_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #2
+    file3 TINYTEXT NULL,                                  -- File name for a single uploaded file #3
+    file3_descr TINYTEXT NULL,                            -- Description for file uploaded in field "file" #3
+    url TINYTEXT NULL,                                    -- Event URL
+    ticket_url TINYTEXT NULL,                             -- Ticket URL
+    registration_url TINYTEXT NULL,                       -- Registration URL
+    cost TINYTEXT NULL,                                   -- Description of event cost
+    admin_ref_type INT NULL,                              -- Type of admin contact if using a member contact
+    admin_ref_dest INT NULL,                              -- Pointer to admin contact record if using a member contact
+    admin_name TINYTEXT NULL,                             -- Admin Contact Name if not using a member contact
+    admin_org TINYTEXT NULL,                              -- Admin Contact Organization if not using a member contact
+    admin_email TINYTEXT NULL,                            -- Admin Contact E-Mail if not using a member contact
+    admin_phone TINYTEXT NULL,                            -- Admin Contact Phone if not using a member contact
+    free BOOLEAN NULL,                                    -- Event is Free
+    contact_email TINYTEXT NULL,                          -- Contact E-mail address
+    contact_name TINYTEXT NULL,                           -- Contact name
+    contact_phone TINYTEXT NULL,                          -- Event Phone
+    use_member_location BOOLEAN NULL,                     -- Use location of the member (if provided) rather than location table data
+    other_ref_dest INT NULL,                              -- Set location of the event to another member
+    old_event_id INT NULL,                                -- ID of event from old site for reference
+    ical_uid TINYTEXT NULL,                               -- The ical UID for this event.
+    notes TEXT NULL,                                      -- Internal notes for this event
+    PRIMARY KEY (id),
+    INDEX(custom_time),
+    INDEX(root_event),
+    INDEX(ref_type),
+    INDEX(ref_dest),
+    INDEX(featured),
+    INDEX(slideshow),
+    INDEX(major)
+);
+
+----
+
+-- Event Management Settings
+CREATE TABLE {prefix}management (
+    id INT NOT NULL AUTO_INCREMENT,
+    canonical_event_page TINYTEXT NULL,                   -- Canonical page slug for event detail
+    pdf_logo TINYTEXT NULL,                               -- Image for the Top of the PDF
+    footer_text TINYTEXT NULL,                            -- Image for the Top of the PDF
+    to_email TINYTEXT NULL,                               -- Email address of the recipient
+    from_email TINYTEXT NULL,                             -- Email address of the sender
+    email_notification TEXT NULL,                         -- Email notification message
+    calendar_view TINYTEXT NULL,                          -- Default calendar view
+    event_default_state TINYTEXT NULL,                    -- Default calendar view
+    term_event_amenities_singular TINYTEXT NULL,          -- Singular term to use for event amenities
+    term_event_amenities_plural TINYTEXT NULL,            -- Plural term to use for event amenities
+    use_event_amenities BOOLEAN NULL,                     -- Whether amenities are used at all for events
+    use_venue_locations BOOLEAN NULL,                     -- To use Other members as the location of event
+    member_events_allowed BOOLEAN NULL,                   -- If members are allowed to add events.
+    member_events_moderated BOOLEAN DEFAULT '0',          -- If member edits are moderated.
+    ical_feed_image_size TINYTEXT NULL,                   -- Image size to use in iCal Feed
+    event_display_member_message BOOLEAN DEFAULT '0',     -- Boolean to show member message or not
+    event_member_message TEXT NULL,                       -- Member Message
+    event_add_button_color TINYTEXT NULL,                 -- Color of the search button
+    event_add_button_hidden BOOLEAN NULL,                 -- Option to hide the add event button
+    event_back_to_search_color TINYTEXT NULL,             -- Background Color of the search
+    agenda_date_background_color TINYTEXT NULL,           -- Background Color of the date
+    agenda_date_text_color TINYTEXT NULL,                 -- Text Color of the date
+    agenda_title_color TINYTEXT NULL,                     -- Color of the Event Title
+    agenda_container_background_color TINYTEXT NULL,      -- Event Container Background Color
+    agenda_view_max_width TINYTEXT NULL,                  -- Max Width of agenda view
+    PRIMARY KEY (id)
+);
+
+----
+
+-- Set default event management entry
+INSERT INTO {prefix}management
+    ( id, canonical_event_page, term_event_amenities_singular, term_event_amenities_plural, member_events_allowed, ical_feed_image_size, event_display_member_message, event_member_message )
+   VALUES
+    ( 1, 'event-detail', 'Amenity', 'Amenities', 1, 'large', 0, '')
+;
+
+----
+
+-- Event iCal Feed imports
+CREATE TABLE {prefix}feed_import (
+    id INT NOT NULL AUTO_INCREMENT,
+    feed_url TEXT NOT NULL,            -- The ical feed url to import
+    created DATETIME NULL,             -- The date this feed was created
+    updated DATETIME NULL,             -- Last time this feed was updated
+    duration INT NULL,                 -- The time it took to fetch the feed
+    events INT NULL,                   -- The number of events last fetched
+    PRIMARY KEY (id)
+);
+
+----
+
+-- Event Email Notifications
+CREATE TABLE {prefix}email_notifications (
+    id INT NOT NULL AUTO_INCREMENT,
+    declined_message TEXT NULL,    -- Event declined message
+    approved_message TEXT NULL,    -- Event approved message
+    to_email TINYTEXT NULL,        -- To Email Address
+    from_email TINYTEXT NULL,      -- From Email Address
+    email_notification TEXT NULL,  -- Email notification message
+    PRIMARY KEY (id)
+);
+
+----
+
+-- Set default event email settings
+INSERT INTO {prefix}email_notifications
+    ( id, declined_message, approved_message )
+   VALUES
+    ( 1, 'The Event parameters do not comply with our event guidelines.', 'Your event has been approved.' )
+;
index 5be8b57..fea850f 100644 (file)
@@ -43,5 +43,6 @@ $glmMembersEventsDbVersions = array(
     '0.1.5' => array('version' => '0.1.5', 'tables' => 13, 'date' => '02/21/2018'),
     '0.1.6' => array('version' => '0.1.6', 'tables' => 13, 'date' => '08/06/2018'),
     '0.1.7' => array('version' => '0.1.7', 'tables' => 13, 'date' => '09/14/2018'),
+    '0.1.8' => array('version' => '0.1.8', 'tables' => 13, 'date' => '10/09/2018'),
 );
 
diff --git a/setup/databaseScripts/steve-V0.1.8.sql b/setup/databaseScripts/steve-V0.1.8.sql
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/setup/databaseScripts/update_database_V0.1.8.sql b/setup/databaseScripts/update_database_V0.1.8.sql
new file mode 100644 (file)
index 0000000..ecb3ccc
--- /dev/null
@@ -0,0 +1,41 @@
+-- Gaslight Media Members Database  - Events Add-On
+-- File Created: 09/14/18
+-- Database Version: 0.1.7
+-- Database Update From Previous Version Script
+--
+-- To permit each query below to be executed separately,
+-- all queries must be separated by a line with four dashes
+
+ALTER TABLE {prefix}management ADD COLUMN event_add_button_color TINYTEXT NULL;                 -- Color of the search button
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN event_add_button_hidden BOOLEAN NULL;                 -- Option to hide the add event button
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN event_back_to_search_color TINYTEXT NULL;             -- Background Color of the search
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN agenda_date_background_color TINYTEXT NULL;           -- Background Color of the date
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN agenda_date_text_color TINYTEXT NULL;                 -- Text Color of the date
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN agenda_title_color TINYTEXT NULL;                     -- Color of the Event Title
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN agenda_container_background_color TINYTEXT NULL;      -- Event Container Background Color
+
+----
+
+ALTER TABLE {prefix}management ADD COLUMN agenda_view_max_width TINYTEXT NULL;                  -- Max Width of agenda view
+
+----
+
+UPDATE {prefix}management SET event_add_button_hidden = false;
index a446ed8..614faaa 100644 (file)
                             </td>
                         </tr>
 
+                        <tr>
+                            <th>Front-End Event Options</th>
+                            <td>
+                                <label> <input type="checkbox" name="event_add_button_hidden" {if $eventsSettings.fieldData.event_add_button_hidden.value}checked{/if}> Hide Add Event Button</label><br><br>
+                            </td>
+                        </tr>
+                        <tr>
+                            <th>Front-End View Styles</th>
+                            <td>
+                                <table class="glm-admin-table">
+                                    <tr>
+                                        <th {if $eventsSettings.fieldRequired.agenda_view_max_width}class="glm-required"{/if}>Max Width for Agenda View:</th>
+                                        <td {if $eventsSettings.fieldFail.agenda_view_max_width}class="glm-form-bad-input"{/if}>
+                                            <input type="text" name="agenda_view_max_width"
+                                            value="{$eventsSettings.fieldData.agenda_view_max_width}"
+                                            pattern="[0-9]*(%|px)"
+                                            title="Only % or px"
+                                            placeholder="1000px"
+                                            />
+                                            <input type="hidden" name="event_add_button_color" value="" />
+                                            <input type="hidden" name="event_back_to_search_color" value="" />
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th {if $eventsSettings.fieldRequired.agenda_date_background_color}class="glm-required"{/if}>Event Date Background Color:</th>
+                                        <td {if $eventsSettings.fieldFail.agenda_date_background_color}class="glm-form-bad-input"{/if}>
+                                            <input type="text" data-wcp-format="css" class="colorpicker" name="agenda_date_background_color" value="{$eventsSettings.fieldData.agenda_date_background_color}" placeholder="#37426E" />
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th {if $eventsSettings.fieldRequired.agenda_date_text_color}class="glm-required"{/if}>Event Date Text Color:</th>
+                                        <td {if $eventsSettings.fieldFail.agenda_date_text_color}class="glm-form-bad-input"{/if}>
+                                            <input type="text" data-wcp-format="css" class="colorpicker" name="agenda_date_text_color" value="{$eventsSettings.fieldData.agenda_date_text_color}" placeholder="#ffffff" />
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th {if $eventsSettings.fieldRequired.agenda_title_color}class="glm-required"{/if}>Event Title Color:</th>
+                                        <td {if $eventsSettings.fieldFail.agenda_title_color}class="glm-form-bad-input"{/if}>
+                                            <input type="text" data-wcp-format="css" class="colorpicker" name="agenda_title_color" value="{$eventsSettings.fieldData.agenda_title_color}" placeholder="#37426E" />
+                                        </td>
+                                    </tr>
+                                    <tr>
+                                        <th {if $eventsSettings.fieldRequired.agenda_container_background_color}class="glm-required"{/if}>Event Container Background Color:</th>
+                                        <td {if $eventsSettings.fieldFail.agenda_container_background_color}class="glm-form-bad-input"{/if}>
+                                            <input type="text" data-wcp-format="css" class="colorpicker" name="agenda_container_background_color" value="{$eventsSettings.fieldData.agenda_container_background_color}" placeholder="#37426E" />
+                                        </td>
+                                    </tr>
+                                </table>
+                            </td>
+                        </tr>
+
                         <tr>
                             <td> <h2> {$terms.term_member_cap} Message </h2> </td>
                             <td> <p> This will display a message to your members when they add or edit one of their events. </p> </td>
 
         });
 
+        /**
+         * Setup the color pickers.
+         */
+        $('.colorpicker').wheelColorPicker();
+
         // Flash certain elements for a short time after display
         $(".glm-flash-updated").fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500).fadeIn(500).fadeOut(500);
 
index f239435..d11a605 100644 (file)
+<style>
+#glm-event-wrapper .glm-event-cal-new {
+    border: 1px solid {if $settings.agenda_date_background_color}{$settings.agenda_date_background_color}{else}#37426E{/if};
+    font-size: 19px;
+    margin: 0 auto;
+    width: 275px;
+    border-radius: 5px;
+    text-align: center;
+}
+.glm-event-cal-day-container-new {
+    background: {if $settings.agenda_date_background_color}{$settings.agenda_date_background_color}{else}#37426E{/if};
+    color: {if $settings.agenda_date_text_color}{$settings.agenda_date_text_color}{else}white{/if};
+    padding: 10px 20px;
+}
+.glm-event-day-event::before {
+    display: none !important;
+}
+#glm-event-wrapper .glm-event-day-event {
+    border: 1px solid {if $settings.agenda_container_background_color}{$settings.agenda_container_background_color}{else}#37426E{/if} !important;
+}
+#glm-event-wrapper h2 {
+    color: {if $settings.agenda_title_color}{$settings.agenda_title_color}{else}#37426E{/if};
+    font-weight: bold;
+}
+{if $settings.event_add_button_hidden}
+#glm-event-add-event {
+    display: none !important;
+}
+{/if}
+</style>
+
 <div id="glm-event-wrapper">
-    <div class="row collapse">
-        {include file='front/events/searchForm.html'}
-    </div>
+        <div class="row">
+            {include file='front/events/searchForm.html'}
+        </div>
+
+        <div id="glm-event-agenda-view" class="row" style="max-width: {if $settings.agenda_view_max_width}{$settings.agenda_view_max_width}{else}1000px{/if}; margin: 0 auto;">
+            <div class="small-12 columns">
+                {foreach $eventsByDate as $date => $key}
+                    <div class="row glm-event-day-row-new open">
+                        <div class="small-12 medium-12 columns">
+                            <div class="glm-event-cal-new">
+                                <div class="glm-event-cal-day-container-new">
+                                    <span>{$date|date_format:"%A %B %e"}</span>
+                                </div>
 
-    <div id="glm-event-agenda-view" class="row">
-        <div class="small-12 columns">
-            {foreach $eventsByDate as $date => $key}
-                <div class="row glm-event-day-row">
-                    <div class="small-12 medium-2 columns">
-                        <div class="glm-event-cal">
-                            <div class="glm-event-cal-month">{$date|date_format:"%b"}</div>
-                            <div class="glm-event-cal-day-container">
-                                <span class="glm-event-cal-day">{$date|date_format:"%e"}</span>
-                                <span class="glm-event-cal-dow">{$date|date_format:"%a"}</span>
                             </div>
                         </div>
-                    </div>
-                    <div class="small-12 medium-10 columns">
-                        {foreach $key as $events}
-                            {foreach $events as $event}
-                                {$showTime = true}
-                            <div class="row glm-event-day-event">
-                                <div class="small-12 columns">
-                                    <h2 class="agenda-event-name">{$event.name}</h2>
-                                    {$eventDateTime = $event.starting_date|date_format:"%I:%M %P"}
-                                        {foreach $event.recurrences as $rec}
-                                            {$start_time = $rec.start_time.time}
-                                            {if $start_time == $eventDateTime}
-                                                {if ($rec.from_date.timestamp == $rec.to_date.timestamp && $rec.from_date.timestamp == $date)
-                                                or ($date <= $rec.to_date.timestamp && $date >= $rec.from_date.timestamp )}
-                                                    <div class='date-range-container'>
-                                                        {if !$event.all_day}
-                                                            {$showTime = false}
-                                                            {$event_start_time = $event.starting_date|date_format:"%m/%d/%Y"}
-                                                            {$event_start_time = $event_start_time|cat:' '}
-                                                            {$event_start_time = $event_start_time|cat:$rec.start_time.time}
-                                                            {$event_end_time = $event.starting_date|date_format:"%m/%d/%Y"}
-                                                            {$event_end_time = $event_end_time|cat:' '}
-                                                            {$event_end_time = $event_end_time|cat:$rec.end_time.time}
-                                                            {if $event_start_time|date_format:"%l:%M %P" == $event_end_time|date_format:"%l:%M %P" or $rec.start_time_only.value == '1'}
-                                                                {$event_start_time|date_format:"%l:%M %P"}
-                                                            {else}
-                                                                {$event_start_time|date_format:"%l:%M %P"} - {$event_end_time|date_format:"%l:%M %P"}
+                        <div class="small-12 medium-12 columns">
+                            {foreach $key as $events}
+                                {foreach $events as $event}
+                                    {$showTime = true}
+                                <div class="row glm-event-day-event" data-url="{$siteBaseUrl}{$pageSlug}/{if !$customPage}{$event.name_slug}/{else}?eventId={$event.id}{/if}">
+                                    <div class="small-12 large-8 columns">
+
+                                        <a class="glm-link" href="{$siteBaseUrl}{$pageSlug}/{if !$customPage}{$event.name_slug}/{else}?eventId={$event.id}{/if}">
+                                            <h2 class="agenda-event-name">{$event.name}</h2>
+                                        </a>
+
+                                        {$eventDateTime = $event.starting_date|date_format:"%I:%M %P"}
+                                            {foreach $event.recurrences as $rec}
+                                                {$start_time = $rec.start_time.time}
+                                                {if $start_time == $eventDateTime}
+                                                    {if ($rec.from_date.timestamp == $rec.to_date.timestamp && $rec.from_date.timestamp == $date)
+                                                    or ($date <= $rec.to_date.timestamp && $date >= $rec.from_date.timestamp )}
+                                                        <div class='date-range-container'>
+                                                            {if !$event.all_day}
+                                                                {$showTime = false}
+                                                                {if $event.starting_date|date_format:"%l:%M %P" == $event.ending_date|date_format:"%l:%M %P"}
+                                                                    {$event.starting_date|date_format:"%l:%M %P"}
+                                                                {else}
+                                                                    {$event.starting_date|date_format:"%l:%M %P"} - {$event.ending_date|date_format:"%l:%M %P"}
+                                                                {/if}
+
+                                                            {/if}
+                                                            {if $rec.name && $rec.name != 'Imported' && $rec.name != 'Imported Event Schedule' }
+                                                                {$rec.name}
                                                             {/if}
+                                                            ( {if $event.times|@count > 1} Occurring {/if}
+                                                            {$rec.from_date.date} <span class="agenda-event-recur-dates">
+                                                              {if $rec.from_date.date != $rec.to_date.date}
+                                                                - {$rec.to_date.date}
+                                                               {else if $rec.specific_dates}
+                                                                - {$rec.specific_dates|@end|date_format:"%m/%d/%Y"}
+                                                              {/if}
+                                                            </span>
+
+                                                        {if $rec.day_of_week.names|@count < 7 && $rec.day_of_week.names|@count > 0 }
+                                                        <span class="agenda-days-of-week">
+                                                            on
+                                                            {foreach $rec.day_of_week.names as $day}
+                                                                {if $day == "Thursday"}
+                                                                    <span class="agenda-event-weekday"> {$day|substr:0:4} </span>
+                                                                 {else}
+                                                                    <span class="agenda-event-weekday"> {$day|substr:0:3} </span>
+                                                                 {/if}
+                                                             {/foreach}
 
+                                                        </span>
                                                         {/if}
-                                                        {if $rec.name && $rec.name != 'Imported' && $rec.name != 'Imported Event Schedule' }
-                                                            {$rec.name}
+                                                         )
+                                                        </div>
+                                                        {if $event.locations.city.name}
+                                                           <div class="glm-agenda-city">City: {$event.locations.city.name}</div>
                                                         {/if}
-                                                        ( {if $event.times|@count > 1} Occurring {/if}
-                                                        {$rec.from_date.date} <span class="agenda-event-recur-dates">
-                                                          {if $rec.from_date.date != $rec.to_date.date}
-                                                            - {$rec.to_date.date}
-                                                           {else if $rec.specific_dates}
-                                                            - {$rec.specific_dates|@end|date_format:"%m/%d/%Y"}
-                                                          {/if}
-                                                        </span>
-
-                                                    {if $rec.day_of_week.names|@count < 7 && $rec.day_of_week.names|@count > 0 }
-                                                    <span class="agenda-days-of-week">
-                                                        on
-                                                        {foreach $rec.day_of_week.names as $day}
-                                                            {if $day == "Thursday"}
-                                                                <span class="agenda-event-weekday"> {$day|substr:0:4} </span>
-                                                             {else}
-                                                                <span class="agenda-event-weekday"> {$day|substr:0:3} </span>
-                                                             {/if}
-                                                         {/foreach}
-
-                                                    </span>
                                                     {/if}
-                                                     )
-                                                    </div>
+                                                {/if}
+                                            {/foreach}
+                                        <div class="day-times-wrapper">
+                                            {if !$event.all_day && $showTime}
+                                                {if $event.starting_date|date_format:"%l:%M %P" == $event.ending_date|date_format:"%l:%M %P"}
+                                                <div class="agenda-event-times">{$event.starting_date|date_format:"%l:%M %P"}</div>
+                                                {else}
+                                                <div class="agenda-event-times"> {$event.starting_date|date_format:"%l:%M %P"} - {$event.ending_date|date_format:"%l:%M %P"}</div>
                                                 {/if}
                                             {/if}
-                                        {/foreach}
-                                    <div class="day-times-wrapper">
-                                        {if !$event.all_day && $showTime}
-                                            {if $event.starting_date|date_format:"%l:%M %P" == $event.ending_date|date_format:"%l:%M %P"}
-                                            <div class="agenda-event-times">{$event.starting_date|date_format:"%l:%M %P"}</div>
-                                            {else}
-                                            <div class="agenda-event-times"> {$event.starting_date|date_format:"%l:%M %P"} - {$event.ending_date|date_format:"%l:%M %P"}</div>
+                                        </div>
+                                        <div class="event-contents clearfix">
+                                            {if $event.locations}
+                                                <div class="glm-agenda-city">Location: {$event.locations.name}</div>
                                             {/if}
-                                        {/if}
-                                    </div>
-                                    <div class="event-content glm-hide clearfix">
-                                        {if $event.image}<img style="float:right;" data-src="{$imgUrl}{$event.image}">{/if}
-                                        {$event.intro}
-                                        <div>
-                                            <a class="glm-read-more" href="{$siteBaseUrl}{$pageSlug}/{if !$customPage}{$event.name_slug}/{else}?eventId={$event.id}{/if}">Read More</a>
+
+                                            {$event.intro}
+
                                         </div>
                                     </div>
+                                    <div class="small-12 large-4 columns glm-events-agenda-image">
+                                        {if $event.image}<img class="" style="float:none;" src="{$imgUrl}{$event.image}">{/if}
+                                    </div>
                                 </div>
-                            </div>
+                                {/foreach}
                             {/foreach}
-                        {/foreach}
+                        </div>
                     </div>
-                </div>
-            {/foreach}
+                {/foreach}
 
+            </div>
         </div>
-    </div>
-    {assign var="current_year" value=$smarty.now|date_format:"%Y"}
-    {assign var="current_month" value=$smarty.now|date_format:"%m"}
-    <div id="event-date-toggle-container">
-        <label class="date-toggle-label" for="#event-date-toggle"> Select a Date <label>
-        <select id="event-date-toggle">
-            <option value="" disabled selected> Select Month</option>
-            {foreach $months as $year=>$month}{
-                {foreach $month as $m=>$value}
-                    {if $current_month == $value.date && $current_year == $year}
-                        <option value="{$year}-{$value.date}-01" selected> {$value.name} - {$year}</option>
-                    {else}
-                        <option value="{$year}-{$value.date}-01"> {$value.name} - {$year}</option>
-                    {/if}
+        {assign var="current_year" value=$smarty.now|date_format:"%Y"}
+        {assign var="current_month" value=$smarty.now|date_format:"%m"}
+        <div id="event-date-toggle-container">
+            <label class="date-toggle-label" for="#event-date-toggle"> Select a Date <label>
+            <select id="event-date-toggle">
+                <option value="" disabled selected> Select Month</option>
+                {foreach $months as $year=>$month}{
+                    {foreach $month as $m=>$value}
+                        {if $current_month == $value.date && $current_year == $year}
+                            <option value="{$year}-{$value.date}-01" selected> {$value.name} - {$year}</option>
+                        {else}
+                            <option value="{$year}-{$value.date}-01"> {$value.name} - {$year}</option>
+                        {/if}
+                    {/foreach}
                 {/foreach}
-            {/foreach}
-        </select>
-    </div>
-    <!-- Calendar -->
-    <div id="eventCalendar" style='position: relative;'>
-        <div class='event-overlay'>
-            <div class="loading"></div>
+            </select>
+        </div>
+        <!-- Calendar -->
+        <div id="eventCalendar" style='position: relative;'>
+            <div class='event-overlay'>
+                <div class="loading"></div>
+            </div>
         </div>
     </div>
-</div>
-
-
-
-<script type="text/javascript">
-    jQuery(document).ready(function($) {
-
-        var fullCalendarLoaded  = false;
-        var recurrencesInited   = false;
-        var locationsInited     = false;
-        var table               = 'glm-table-calendar';
-        var post_start, post_end, month_obj;
-        var rows                = $(".glm-event-day-row");
-        var agenda              = $("#glm-event-agenda-view");
-        var view_select         = $("#glm-event-view-select");
-        var search              = $("#glm-events-search-form");
-        var calendar            = $("#eventCalendar");
-        var event_search        = $(".glm-search-icon");
-        var main_content        = $("#main-content");
-        var member_id           = '{$memberId}';
-        var view                = '{$cal_view}';
-        var months              = '{$json_months}';
-        var category            = $('#glm-event-category').val();
-        var current_month       = $('{$current_month}');
-        var current_year        = $('{$current_year}');
-        var custom_page         = '{$customPage}';
-        var page_slug           = '{$pageSlug}';
-        var retain_date;
-
-        if(!member_id){
-            member_id = null;
-        }
-
-        // add 7 days to ensure we always get the current month and not any other month view that may
-        // be visible (last few days of previous month or first few days of next month)
-        function get_current_view(){
+
+    <script type="text/javascript">
+        jQuery(document).ready(function($) {
+
+            var fullCalendarLoaded  = false;
+            var recurrencesInited   = false;
+            var locationsInited     = false;
+            var table               = 'glm-table-calendar';
+            var post_start, post_end, month_obj;
+            var rows                = $(".glm-event-day-row");
+            var agenda              = $("#glm-event-agenda-view");
+            var view_select         = $("#glm-event-view-select");
+            var search              = $("#glm-events-search-form");
+            var calendar            = $("#eventCalendar");
+            var event_search        = $(".glm-search-icon");
+            var main_content        = $("#main-content");
+            var member_id           = '{$memberId}';
+            var view                = '{$cal_view}';
+            var months              = '{$json_months}';
+            var category            = $('#glm-event-category').val();
+            var current_month       = $('{$current_month}');
+            var current_year        = $('{$current_year}');
+            var custom_page         = '{$customPage}';
+            var page_slug           = '{$pageSlug}';
             var retain_date;
-            var calCurrDate = $('#eventCalendar').fullCalendar('getView').start;
-            var d = moment(calCurrDate).add(7, 'days').format('YYYY-MM-01');
-
-            return retain_date = d;
-        }
-        function in_mobile(){
-            if($(window).width() < 641 ){
-                return true;
-            } else {
-                return false;
+
+            if(!member_id){
+                member_id = null;
             }
-        }
 
-        $('#glm-event-from').on("change", function(){
-            var from_date = $(this).val();
-            $('#glm-event-to').val(from_date);
+            // add 7 days to ensure we always get the current month and not any other month view that may
+            // be visible (last few days of previous month or first few days of next month)
+            function get_current_view(){
+                var retain_date;
+                var calCurrDate = $('#eventCalendar').fullCalendar('getView').start;
+                var d = moment(calCurrDate).add(7, 'days').format('YYYY-MM-01');
 
-        });
+                return retain_date = d;
+            }
+            function in_mobile(){
+                if($(window).width() < 641 ){
+                    return true;
+                } else {
+                    return false;
+                }
+            }
 
-        months = JSON.parse(months);
+            $('#glm-event-from').on("change", function(){
+                var from_date = $(this).val();
+                $('#glm-event-to').val(from_date);
 
-        event_search.on('click', function(){
-            sessionStorage.override = 'yes';
-        });
+            });
 
-        if( sessionStorage.view && sessionStorage.override === 'no' && !in_mobile()){
-            view = sessionStorage.view;
-        } else if( sessionStorage.override === 'yes' || in_mobile()){
-            view = 'agenda';
-        }
-
-        // AJAX FUNCTION ************************************************
-        // Get calendar month via AJAX
-        function eventsCalMonthAJAX(month) {
-            var dat = {
-
-                action: 'glm_members_admin_ajax',
-                glm_action: 'eventsCalMonthAJAX',
-                month: month,
-                category: category,
-                member_id: member_id,
-                custom_page: custom_page,
-                page_slug: page_slug
-            };
-            $('.fc-event').remove();
-
-             jQuery.ajax({
-                type: 'POST',
-                url: '{$ajaxUrl}',
-                data: dat,
-                beforeSend: function(){
-                    $(".event-overlay").show();
-                    $(".loading").show();
+            months = JSON.parse(months);
+
+            event_search.on('click', function(){
+                sessionStorage.override = 'yes';
+            });
+
+            if( sessionStorage.view && sessionStorage.override === 'no' && !in_mobile()){
+                view = sessionStorage.view;
+            } else if( sessionStorage.override === 'yes' || in_mobile()){
+                view = 'agenda';
+            }
+
+            // AJAX FUNCTION ************************************************
+            // Get calendar month via AJAX
+            function eventsCalMonthAJAX(month) {
+                var dat = {
+
+                    action: 'glm_members_admin_ajax',
+                    glm_action: 'eventsCalMonthAJAX',
+                    month: month,
+                    category: category,
+                    member_id: member_id,
+                    custom_page: custom_page,
+                    page_slug: page_slug
+                };
+                $('.fc-event').remove();
+
+                 jQuery.ajax({
+                    type: 'POST',
+                    url: '{$ajaxUrl}',
+                    data: dat,
+                    beforeSend: function(){
+                        $(".event-overlay").show();
+                        $(".loading").show();
+                    },
+                    complete: function(){
+                        $(".event-overlay").hide();
+                        $(".loading").hide();
+                    },
+                    cache: false,
+                    success: function (response){
+                        var buildingEvents = [];
+                        //var events_obj = jQuery.parseJSON(response);
+                        var events_obj = response;
+                        var events = events_obj.events;
+                        buildingEvents = $.map(events, function(i, val) {
+                            return {
+                                title:   i.title,
+                                start:   i.start,
+                                end:     i.last,
+                                allDay:  i.all_day,
+                                url:     i.url,
+                                recurs:  i.recurs
+                            };
+                        });
+                        $("#eventCalendar").fullCalendar('removeEvents');
+                        $("#eventCalendar").fullCalendar('addEventSource', buildingEvents);
+                    }
+                });
+                var timestamp = $.now();
+                var url = '{$ajaxUrl}' + '?t=' + timestamp;
+            }
+            var height = 900;
+            // Calendar *****************************************************
+            $('#eventCalendar').fullCalendar({
+                eventOrder: 'allDay',
+                height: height,
+                contentHeight: height,
+                eventRender: function(event, element){
+                    element.attr('title', event.title);
                 },
-                complete: function(){
-                    $(".event-overlay").hide();
-                    $(".loading").hide();
+                viewRender: function (view, element){
+                    post_start = view.start._d;
+                    post_end = view.end._d;
+                    {literal}month_obj = {start: view.start._d, last: view.end._d};{/literal}
                 },
-                cache: false,
-                success: function (response){
-                    var buildingEvents = [];
-                    //var events_obj = jQuery.parseJSON(response);
-                    var events_obj = response;
-                    var events = events_obj.events;
-                    buildingEvents = $.map(events, function(i, val) {
-                        return {
-                            title:   i.title,
-                            start:   i.start,
-                            end:     i.last,
-                            allDay:  i.all_day,
-                            url:     i.url,
-                            recurs:  i.recurs
-                        };
-                    });
-                    $("#eventCalendar").fullCalendar('removeEvents');
-                    $("#eventCalendar").fullCalendar('addEventSource', buildingEvents);
-                }
-            });
-            var timestamp = $.now();
-            var url = '{$ajaxUrl}' + '?t=' + timestamp;
-        }
-        var height = 900;
-        // Calendar *****************************************************
-        $('#eventCalendar').fullCalendar({
-            eventOrder: 'allDay',
-            height: height,
-            contentHeight: height,
-            eventRender: function(event, element){
-                element.attr('title', event.title);
-            },
-            viewRender: function (view, element){
-                post_start = view.start._d;
-                post_end = view.end._d;
-                {literal}month_obj = {start: view.start._d, last: view.end._d};{/literal}
-            },
-            header: {
-                left: 'prev,next,today',
-                center: 'title',
-                right: ''
-            },
-            dayClick: function(date, jsEvent, view) {
-                //$('#eventCalendar').fullCalendar('changeView', 'agendaDay');
-                //$('#eventCalendar').fullCalendar('gotoDate', date);
-            },
-            editable: false,
-            handleWindowResize: true,
-            lazyFetching: true,
-            eventClick : function (event){
-
-                sessionStorage.current_calendar_view = get_current_view();
-
-
-                if(custom_page !== ''){
-                    location.href = event.url + '&glm_event_from=' + event.start.format();
-                } else {
-                    location.href = event.url + '?glm_event_from=' + event.start.format();
-                }
+                header: {
+                    left: 'prev,next,today',
+                    center: 'title',
+                    right: ''
+                },
+                dayClick: function(date, jsEvent, view) {
+                    //$('#eventCalendar').fullCalendar('changeView', 'agendaDay');
+                    //$('#eventCalendar').fullCalendar('gotoDate', date);
+                },
+                editable: false,
+                handleWindowResize: true,
+                lazyFetching: true,
+                eventClick : function (event){
+
+                    sessionStorage.current_calendar_view = get_current_view();
 
-                return false;
-            },
-            eventLimit: true,
-            views: {
-                month: {
-                    eventLimit: 4,
-                    eventBackgroundColor: 'transparent',
-                    eventTextColor: '#3A87AD',
-                    eventBorderColor: 'transparent',
+
+                    if(custom_page !== ''){
+                        location.href = event.url + '&glm_event_from=' + event.start.format();
+                    } else {
+                        location.href = event.url + '?glm_event_from=' + event.start.format();
+                    }
+
+                    return false;
                 },
-                agenda: {
-                    eventLimit: 4
+                eventLimit: true,
+                views: {
+                    month: {
+                        eventLimit: 4,
+                        eventBackgroundColor: 'transparent',
+                        eventTextColor: '#3A87AD',
+                        eventBorderColor: 'transparent',
+                    },
+                    agenda: {
+                        eventLimit: 4
+                    }
                 }
-            }
-        });
+            });
 
-        if(view === "calendar"){
-            eventsCalMonthAJAX(month_obj);
-            main_content.removeClass("small-8").addClass("small-12");
-            calendar.show();
-            view_select.val('calendar');
-
-        } else if ( view === "agenda"){
-            calendar.hide();
-            agenda.css("display", "block");
-            view_select.val('agenda');
-        }
-        $("#glm-event-view-select").on("change", function (){
-            sessionStorage.override = 'no';
-            sessionStorage.setItem("view", $(this).attr("value"));
-            if($("#glm-event-view-select option:selected").val() === "calendar"){
+            if(view === "calendar"){
                 eventsCalMonthAJAX(month_obj);
-                agenda.css("display","none");
-                calendar.fadeIn('slow', function(){});
-                calendar.fullCalendar('render');
+                main_content.removeClass("small-8").addClass("small-12");
+                calendar.show();
+                view_select.val('calendar');
 
-            } else if ( $("#glm-event-view-select option:selected").val() === "agenda"){
+            } else if ( view === "agenda"){
+                calendar.hide();
                 agenda.css("display", "block");
-                calendar.fadeOut('slow', function(){});
+                view_select.val('agenda');
             }
-        });
-        $(window).on("load", function(){
+            $("#glm-event-view-select").on("change", function (){
+                sessionStorage.override = 'no';
+                sessionStorage.setItem("view", $(this).attr("value"));
+                if($("#glm-event-view-select option:selected").val() === "calendar"){
+                    eventsCalMonthAJAX(month_obj);
+                    agenda.css("display","none");
+                    calendar.fadeIn('slow', function(){});
+                    calendar.fullCalendar('render');
+
+                } else if ( $("#glm-event-view-select option:selected").val() === "agenda"){
+                    agenda.css("display", "block");
+                    calendar.fadeOut('slow', function(){});
+                }
+            });
+            $(window).on("load", function(){
 
-            if(sessionStorage.current_calendar_view){
-                $('#event-date-toggle option').each(function(){
-                    if( $(this).val() === sessionStorage.current_calendar_view){
-                        $(this).prop('selected', true);
-                    }
-                });
-            }
+                if(sessionStorage.current_calendar_view){
+                    $('#event-date-toggle option').each(function(){
+                        if( $(this).val() === sessionStorage.current_calendar_view){
+                            $(this).prop('selected', true);
+                        }
+                    });
+                }
 
-            $('#event-date-toggle-container').insertAfter( $('.fc-left')).fadeTo('slow', 1);
+                $('#event-date-toggle-container').insertAfter( $('.fc-left')).fadeTo('slow', 1);
 
-            $('#event-date-toggle').on("change", function(){
-                var selected_date = $(this).val();
+                $('#event-date-toggle').on("change", function(){
+                    var selected_date = $(this).val();
 
-                $('#eventCalendar').fullCalendar('gotoDate', selected_date);
+                    $('#eventCalendar').fullCalendar('gotoDate', selected_date);
+                     eventsCalMonthAJAX(month_obj);
+                     if(sessionStorage.current_calendar_view){
+                        sessionStorage.current_calendar_view = '';
+                    }
+                });
+            });
+            if(sessionStorage.current_calendar_view){
+                 $('#eventCalendar').fullCalendar('gotoDate', sessionStorage.current_calendar_view);
                  eventsCalMonthAJAX(month_obj);
-                 if(sessionStorage.current_calendar_view){
+            }
+            // initialize calendar with AJAX calls *************************************
+            $(document).on("click", "button.fc-button", function () {
+                if(sessionStorage.current_calendar_view){
                     sessionStorage.current_calendar_view = '';
                 }
+
+                eventsCalMonthAJAX(month_obj);
             });
-        });
-        if(sessionStorage.current_calendar_view){
-             $('#eventCalendar').fullCalendar('gotoDate', sessionStorage.current_calendar_view);
-             eventsCalMonthAJAX(month_obj);
-        }
-        // initialize calendar with AJAX calls *************************************
-        $(document).on("click", "button.fc-button", function () {
-            if(sessionStorage.current_calendar_view){
-                sessionStorage.current_calendar_view = '';
-            }
 
-            eventsCalMonthAJAX(month_obj);
-        });
+           $('#eventCalendar').find('.fc-today-button').on("click", function(){
+               eventsCalMonthAJAX(month_obj);
+               var current_view = get_current_view();
+               $('#event-date-toggle option').each(function(){
+                    if( $(this).val() === current_view){
+                        $(this).prop('selected', true);
+                    }
+                });
+           });
 
-       $('#eventCalendar').find('.fc-today-button').on("click", function(){
-           eventsCalMonthAJAX(month_obj);
-           var current_view = get_current_view();
-           $('#event-date-toggle option').each(function(){
-                if( $(this).val() === current_view){
-                    $(this).prop('selected', true);
-                }
+            var dates = $(".agenda-event-recur-dates");
+            dates.each( function (){
+               if( $(this).text().indexOf('-') < 0 ){
+                   $(this).remove();
+               }
             });
-       });
 
-        var dates = $(".agenda-event-recur-dates");
-        dates.each( function (){
-           if( $(this).text().indexOf('-') < 0 ){
-               $(this).remove();
-           }
+            // Event agenda view container click, go to event url
+            $('.glm-event-day-event').on("click", function(){
+                window.location = $(this).data('url');
+            });
+
+
         });
+    </script>
 
-    });
-</script>
diff --git a/views/front/events/agenda_old.html b/views/front/events/agenda_old.html
new file mode 100644 (file)
index 0000000..f239435
--- /dev/null
@@ -0,0 +1,376 @@
+<div id="glm-event-wrapper">
+    <div class="row collapse">
+        {include file='front/events/searchForm.html'}
+    </div>
+
+    <div id="glm-event-agenda-view" class="row">
+        <div class="small-12 columns">
+            {foreach $eventsByDate as $date => $key}
+                <div class="row glm-event-day-row">
+                    <div class="small-12 medium-2 columns">
+                        <div class="glm-event-cal">
+                            <div class="glm-event-cal-month">{$date|date_format:"%b"}</div>
+                            <div class="glm-event-cal-day-container">
+                                <span class="glm-event-cal-day">{$date|date_format:"%e"}</span>
+                                <span class="glm-event-cal-dow">{$date|date_format:"%a"}</span>
+                            </div>
+                        </div>
+                    </div>
+                    <div class="small-12 medium-10 columns">
+                        {foreach $key as $events}
+                            {foreach $events as $event}
+                                {$showTime = true}
+                            <div class="row glm-event-day-event">
+                                <div class="small-12 columns">
+                                    <h2 class="agenda-event-name">{$event.name}</h2>
+                                    {$eventDateTime = $event.starting_date|date_format:"%I:%M %P"}
+                                        {foreach $event.recurrences as $rec}
+                                            {$start_time = $rec.start_time.time}
+                                            {if $start_time == $eventDateTime}
+                                                {if ($rec.from_date.timestamp == $rec.to_date.timestamp && $rec.from_date.timestamp == $date)
+                                                or ($date <= $rec.to_date.timestamp && $date >= $rec.from_date.timestamp )}
+                                                    <div class='date-range-container'>
+                                                        {if !$event.all_day}
+                                                            {$showTime = false}
+                                                            {$event_start_time = $event.starting_date|date_format:"%m/%d/%Y"}
+                                                            {$event_start_time = $event_start_time|cat:' '}
+                                                            {$event_start_time = $event_start_time|cat:$rec.start_time.time}
+                                                            {$event_end_time = $event.starting_date|date_format:"%m/%d/%Y"}
+                                                            {$event_end_time = $event_end_time|cat:' '}
+                                                            {$event_end_time = $event_end_time|cat:$rec.end_time.time}
+                                                            {if $event_start_time|date_format:"%l:%M %P" == $event_end_time|date_format:"%l:%M %P" or $rec.start_time_only.value == '1'}
+                                                                {$event_start_time|date_format:"%l:%M %P"}
+                                                            {else}
+                                                                {$event_start_time|date_format:"%l:%M %P"} - {$event_end_time|date_format:"%l:%M %P"}
+                                                            {/if}
+
+                                                        {/if}
+                                                        {if $rec.name && $rec.name != 'Imported' && $rec.name != 'Imported Event Schedule' }
+                                                            {$rec.name}
+                                                        {/if}
+                                                        ( {if $event.times|@count > 1} Occurring {/if}
+                                                        {$rec.from_date.date} <span class="agenda-event-recur-dates">
+                                                          {if $rec.from_date.date != $rec.to_date.date}
+                                                            - {$rec.to_date.date}
+                                                           {else if $rec.specific_dates}
+                                                            - {$rec.specific_dates|@end|date_format:"%m/%d/%Y"}
+                                                          {/if}
+                                                        </span>
+
+                                                    {if $rec.day_of_week.names|@count < 7 && $rec.day_of_week.names|@count > 0 }
+                                                    <span class="agenda-days-of-week">
+                                                        on
+                                                        {foreach $rec.day_of_week.names as $day}
+                                                            {if $day == "Thursday"}
+                                                                <span class="agenda-event-weekday"> {$day|substr:0:4} </span>
+                                                             {else}
+                                                                <span class="agenda-event-weekday"> {$day|substr:0:3} </span>
+                                                             {/if}
+                                                         {/foreach}
+
+                                                    </span>
+                                                    {/if}
+                                                     )
+                                                    </div>
+                                                {/if}
+                                            {/if}
+                                        {/foreach}
+                                    <div class="day-times-wrapper">
+                                        {if !$event.all_day && $showTime}
+                                            {if $event.starting_date|date_format:"%l:%M %P" == $event.ending_date|date_format:"%l:%M %P"}
+                                            <div class="agenda-event-times">{$event.starting_date|date_format:"%l:%M %P"}</div>
+                                            {else}
+                                            <div class="agenda-event-times"> {$event.starting_date|date_format:"%l:%M %P"} - {$event.ending_date|date_format:"%l:%M %P"}</div>
+                                            {/if}
+                                        {/if}
+                                    </div>
+                                    <div class="event-content glm-hide clearfix">
+                                        {if $event.image}<img style="float:right;" data-src="{$imgUrl}{$event.image}">{/if}
+                                        {$event.intro}
+                                        <div>
+                                            <a class="glm-read-more" href="{$siteBaseUrl}{$pageSlug}/{if !$customPage}{$event.name_slug}/{else}?eventId={$event.id}{/if}">Read More</a>
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                            {/foreach}
+                        {/foreach}
+                    </div>
+                </div>
+            {/foreach}
+
+        </div>
+    </div>
+    {assign var="current_year" value=$smarty.now|date_format:"%Y"}
+    {assign var="current_month" value=$smarty.now|date_format:"%m"}
+    <div id="event-date-toggle-container">
+        <label class="date-toggle-label" for="#event-date-toggle"> Select a Date <label>
+        <select id="event-date-toggle">
+            <option value="" disabled selected> Select Month</option>
+            {foreach $months as $year=>$month}{
+                {foreach $month as $m=>$value}
+                    {if $current_month == $value.date && $current_year == $year}
+                        <option value="{$year}-{$value.date}-01" selected> {$value.name} - {$year}</option>
+                    {else}
+                        <option value="{$year}-{$value.date}-01"> {$value.name} - {$year}</option>
+                    {/if}
+                {/foreach}
+            {/foreach}
+        </select>
+    </div>
+    <!-- Calendar -->
+    <div id="eventCalendar" style='position: relative;'>
+        <div class='event-overlay'>
+            <div class="loading"></div>
+        </div>
+    </div>
+</div>
+
+
+
+<script type="text/javascript">
+    jQuery(document).ready(function($) {
+
+        var fullCalendarLoaded  = false;
+        var recurrencesInited   = false;
+        var locationsInited     = false;
+        var table               = 'glm-table-calendar';
+        var post_start, post_end, month_obj;
+        var rows                = $(".glm-event-day-row");
+        var agenda              = $("#glm-event-agenda-view");
+        var view_select         = $("#glm-event-view-select");
+        var search              = $("#glm-events-search-form");
+        var calendar            = $("#eventCalendar");
+        var event_search        = $(".glm-search-icon");
+        var main_content        = $("#main-content");
+        var member_id           = '{$memberId}';
+        var view                = '{$cal_view}';
+        var months              = '{$json_months}';
+        var category            = $('#glm-event-category').val();
+        var current_month       = $('{$current_month}');
+        var current_year        = $('{$current_year}');
+        var custom_page         = '{$customPage}';
+        var page_slug           = '{$pageSlug}';
+        var retain_date;
+
+        if(!member_id){
+            member_id = null;
+        }
+
+        // add 7 days to ensure we always get the current month and not any other month view that may
+        // be visible (last few days of previous month or first few days of next month)
+        function get_current_view(){
+            var retain_date;
+            var calCurrDate = $('#eventCalendar').fullCalendar('getView').start;
+            var d = moment(calCurrDate).add(7, 'days').format('YYYY-MM-01');
+
+            return retain_date = d;
+        }
+        function in_mobile(){
+            if($(window).width() < 641 ){
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        $('#glm-event-from').on("change", function(){
+            var from_date = $(this).val();
+            $('#glm-event-to').val(from_date);
+
+        });
+
+        months = JSON.parse(months);
+
+        event_search.on('click', function(){
+            sessionStorage.override = 'yes';
+        });
+
+        if( sessionStorage.view && sessionStorage.override === 'no' && !in_mobile()){
+            view = sessionStorage.view;
+        } else if( sessionStorage.override === 'yes' || in_mobile()){
+            view = 'agenda';
+        }
+
+        // AJAX FUNCTION ************************************************
+        // Get calendar month via AJAX
+        function eventsCalMonthAJAX(month) {
+            var dat = {
+
+                action: 'glm_members_admin_ajax',
+                glm_action: 'eventsCalMonthAJAX',
+                month: month,
+                category: category,
+                member_id: member_id,
+                custom_page: custom_page,
+                page_slug: page_slug
+            };
+            $('.fc-event').remove();
+
+             jQuery.ajax({
+                type: 'POST',
+                url: '{$ajaxUrl}',
+                data: dat,
+                beforeSend: function(){
+                    $(".event-overlay").show();
+                    $(".loading").show();
+                },
+                complete: function(){
+                    $(".event-overlay").hide();
+                    $(".loading").hide();
+                },
+                cache: false,
+                success: function (response){
+                    var buildingEvents = [];
+                    //var events_obj = jQuery.parseJSON(response);
+                    var events_obj = response;
+                    var events = events_obj.events;
+                    buildingEvents = $.map(events, function(i, val) {
+                        return {
+                            title:   i.title,
+                            start:   i.start,
+                            end:     i.last,
+                            allDay:  i.all_day,
+                            url:     i.url,
+                            recurs:  i.recurs
+                        };
+                    });
+                    $("#eventCalendar").fullCalendar('removeEvents');
+                    $("#eventCalendar").fullCalendar('addEventSource', buildingEvents);
+                }
+            });
+            var timestamp = $.now();
+            var url = '{$ajaxUrl}' + '?t=' + timestamp;
+        }
+        var height = 900;
+        // Calendar *****************************************************
+        $('#eventCalendar').fullCalendar({
+            eventOrder: 'allDay',
+            height: height,
+            contentHeight: height,
+            eventRender: function(event, element){
+                element.attr('title', event.title);
+            },
+            viewRender: function (view, element){
+                post_start = view.start._d;
+                post_end = view.end._d;
+                {literal}month_obj = {start: view.start._d, last: view.end._d};{/literal}
+            },
+            header: {
+                left: 'prev,next,today',
+                center: 'title',
+                right: ''
+            },
+            dayClick: function(date, jsEvent, view) {
+                //$('#eventCalendar').fullCalendar('changeView', 'agendaDay');
+                //$('#eventCalendar').fullCalendar('gotoDate', date);
+            },
+            editable: false,
+            handleWindowResize: true,
+            lazyFetching: true,
+            eventClick : function (event){
+
+                sessionStorage.current_calendar_view = get_current_view();
+
+
+                if(custom_page !== ''){
+                    location.href = event.url + '&glm_event_from=' + event.start.format();
+                } else {
+                    location.href = event.url + '?glm_event_from=' + event.start.format();
+                }
+
+                return false;
+            },
+            eventLimit: true,
+            views: {
+                month: {
+                    eventLimit: 4,
+                    eventBackgroundColor: 'transparent',
+                    eventTextColor: '#3A87AD',
+                    eventBorderColor: 'transparent',
+                },
+                agenda: {
+                    eventLimit: 4
+                }
+            }
+        });
+
+        if(view === "calendar"){
+            eventsCalMonthAJAX(month_obj);
+            main_content.removeClass("small-8").addClass("small-12");
+            calendar.show();
+            view_select.val('calendar');
+
+        } else if ( view === "agenda"){
+            calendar.hide();
+            agenda.css("display", "block");
+            view_select.val('agenda');
+        }
+        $("#glm-event-view-select").on("change", function (){
+            sessionStorage.override = 'no';
+            sessionStorage.setItem("view", $(this).attr("value"));
+            if($("#glm-event-view-select option:selected").val() === "calendar"){
+                eventsCalMonthAJAX(month_obj);
+                agenda.css("display","none");
+                calendar.fadeIn('slow', function(){});
+                calendar.fullCalendar('render');
+
+            } else if ( $("#glm-event-view-select option:selected").val() === "agenda"){
+                agenda.css("display", "block");
+                calendar.fadeOut('slow', function(){});
+            }
+        });
+        $(window).on("load", function(){
+
+            if(sessionStorage.current_calendar_view){
+                $('#event-date-toggle option').each(function(){
+                    if( $(this).val() === sessionStorage.current_calendar_view){
+                        $(this).prop('selected', true);
+                    }
+                });
+            }
+
+            $('#event-date-toggle-container').insertAfter( $('.fc-left')).fadeTo('slow', 1);
+
+            $('#event-date-toggle').on("change", function(){
+                var selected_date = $(this).val();
+
+                $('#eventCalendar').fullCalendar('gotoDate', selected_date);
+                 eventsCalMonthAJAX(month_obj);
+                 if(sessionStorage.current_calendar_view){
+                    sessionStorage.current_calendar_view = '';
+                }
+            });
+        });
+        if(sessionStorage.current_calendar_view){
+             $('#eventCalendar').fullCalendar('gotoDate', sessionStorage.current_calendar_view);
+             eventsCalMonthAJAX(month_obj);
+        }
+        // initialize calendar with AJAX calls *************************************
+        $(document).on("click", "button.fc-button", function () {
+            if(sessionStorage.current_calendar_view){
+                sessionStorage.current_calendar_view = '';
+            }
+
+            eventsCalMonthAJAX(month_obj);
+        });
+
+       $('#eventCalendar').find('.fc-today-button').on("click", function(){
+           eventsCalMonthAJAX(month_obj);
+           var current_view = get_current_view();
+           $('#event-date-toggle option').each(function(){
+                if( $(this).val() === current_view){
+                    $(this).prop('selected', true);
+                }
+            });
+       });
+
+        var dates = $(".agenda-event-recur-dates");
+        dates.each( function (){
+           if( $(this).text().indexOf('-') < 0 ){
+               $(this).remove();
+           }
+        });
+
+    });
+</script>