commit 685def3e067a14a3af0a5f4c028e5dc075f6914f Author: JayWood Date: Tue Jul 9 10:15:02 2013 -0400 Initial Commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a490d17 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +<<<<<<< HEAD +#Dreamweaver .TMP files +*.TMP +*.tmp + +# Log Files +*_log +*.log + +# Backup files +*.bak +*.lwbak + +# Cache +cache +cache_off +cache_disabled +backups +_notes +.ftpquota + +# DW File +dw_php_codehinting.config + +# OS Files +.DS_Store +.DS_Store? +======= +wp-content/uploads + +>>>>>>> 734f97bb1f6b82815b03f904a4b519112724d44c diff --git a/README.md b/README.md new file mode 100644 index 0000000..cca26f6 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +wordpress-content-warning-v3 +============================ + +A WordPress Plugin to warn users of possibly offensive content. diff --git a/class/main.class.php b/class/main.class.php new file mode 100644 index 0000000..72e4a3c --- /dev/null +++ b/class/main.class.php @@ -0,0 +1,193 @@ +__construct(); + } + + public function __construct(){ + // Styling and such + add_action('init', array( &$this, 'register_frontend_data') ); + add_action('wp_enqueue_scripts', array(&$this, 'load_dependancies') ); + + add_action('wp_footer', array(&$this, 'renderDialog')); + + // Post Meta Box for this. + add_action('add_meta_boxes', array(&$this, 'cw_meta')); + add_action('save_post', array(&$this, 'cwv3_meta_save')); + + // AJAX Handle + add_action('wp_ajax_cwv3_ajax', array(&$this, 'handle_ajax')); + add_action('wp_ajax_nopriv_cwv3_ajax', array(&$this, 'handle_ajax')); + } + + public function cw_meta(){ + $scr = array('post', 'page'); + foreach($scr as $screen){ + add_meta_box('cwv3_meta_section', + __('CWV3 Security'), + array(&$this, 'render_metabox'), + $screen, + 'side', + 'high' + ); + } + } + + public function cwv3_meta_save($post_id){ + if('page' == $_POST['post_type']) + if(!current_user_can('edit_page', $post_id) ) + return; + else + if(!current_user_can('edit_post', $post_id) ) + return; + + if(!isset($_POST['cwv3_meta'] ) || ! wp_verify_nonce($_POST['cwv3_meta'], plugin_basename(__FILE__) ) ) + return; + + $mydata = sanitize_text_field($_POST['cwv3_auth']); + update_post_meta($post_id, 'cwv3_auth', $mydata); + + } + + public function handle_ajax(){ + $post_id = intval($_POST['id']); + + check_ajax_referer('cwv3_ajax_'.$post_id, 'nonce'); + + $sw = get_option('cwv3_sitewide') == 'enabled' ? true : false; + $cData = json_decode($_COOKIE['cwv3_auth']); + $time = get_option('cwv3_death'); + $time = time()+($time['multiplier']*$time['time']); + if($_POST['method'] == 'exit'){ + if(get_option('cwv3_denial') == 'enabled'){ + + } + } + + die; + } + + public function load_dependancies(){ + global $post; + + wp_enqueue_style('cwv3_css'); + wp_enqueue_script('cwv3_js'); + + $elink = get_option('cwv3_enter_link'); + $exlink = get_option('cwv3_exit_link'); + $p_ID = (is_home()) ? -1 : (is_attachment() ? $post->post_parent : (is_archive() || is_search()) ? -2 : $post->ID); + + wp_localize_script('cwv3_js', 'cwv3_params', array( + 'action' => 'cwv3_ajax', + 'nonce' => wp_create_nonce('cwv3_ajax_'.$p_ID), + 'admin_url' => admin_url( 'admin-ajax.php' ), + 'id' => $p_ID, + 'sd' => ($this->check_data() !== true) ? true : false, + 'enter' => !empty($elink) ? $elink : '#', + 'exit' => !empty($exlink) ? $exlink : 'http://google.com' + )); + } + + public function register_frontend_data(){ + // Colorbox w/ MIT License + wp_register_style('colorbox', plugins_url('js/colorbox.1.4.14/colorbox.css', dirname(__FILE__)), '', '1.4.14', 'ALL'); + wp_register_script('colorbox_js', plugins_url('js/colorbox.1.4.14/jquery.colorbox-min.js', dirname(__FILE__)), array('jquery'), '1.4.14', true); + + // Main data + wp_register_script('cwv3_js', plugins_url('js/cwv3.js', dirname(__FILE__)), array('colorbox_js'), '1.0', true); + wp_register_style('cwv3_css', plugins_url('css/cwv3.css', dirname(__FILE__)), array('colorbox'), '1.0'); + } + + public function set_cookie($id, $action){ + $cData = json_decode($_COOKIE['cwv3_auth']); + $cData[$id] = $action; + + $time = get_option('cwv3_death'); + setcookie('cwv3_auth', json_encode($cData), ($time['multiplier'] * $time['time'])+time(),'/', COOKIE_DOMAIN, false); + } + + public function check_data(){ + global $post; + + if(is_feed()){ + //Don't want to hender the feed, just in case. + return true; + } + + $cData = json_decode($_COOKIE['cwv3_auth']); + $sw = get_option('cwv3_sitewide'); + $hm = get_option('cwv3_homepage'); + $mi = get_option('cwv3_misc'); + + if($sw[0] == 'enabled'){ + return (!empty($cData['sitewide']) ? $cData['sitewide'] : false); + } + + if(is_home() && $hm[0] == 'enabled'){ + return (!empty($cData['-1']) ? $cData['-1'] : false); + } + + if((is_archive() || is_search()) && $mi[0] == 'enabled'){ + // Protect misc pages aswell + return (!empty($cData['-2']) ? $cData['-2'] : false); + } + + if(is_page()){ + $c = $cData['pages'][$post->ID]; + return(!empty($c) ? $c : false); + } + + $id = (is_attachment() ? $post->post_parent : $post->ID); + // First see if categories are setup in the admin side. + $catData = get_option("cwv3_cat_list"); + $curCat = get_the_category($id); + if(in_array($curCat, $catData)){ + // If the current category is selected in the admin page, that means the administrator wishes to protect it. + // respect the admin's wishes and do it. + return(!empty($cData['categories'][$post->id]) ? $cData['categories'][$id] : false ); + } + // Since that's not the case, we need to check post_meta data and see if this post is protected. + if(get_post_meta($post->ID, 'cwv3_auth', true) == 'yes'){ + return(!empty($cData['posts'][$post->ID]) ? $cData['posts'][$id] : false ); + } + + return true; + } + + public function renderDialog(){ + + $dtype = $this->check_data(); + $etxt = get_option('cwv3_enter_txt'); + $extxt = get_option('cwv3_exit_txt'); + ?> + +
+
+
+
+
+
+
+ + ID, 'cwv3_auth', true);?> + + + value="yes"/> + \ No newline at end of file diff --git a/content-warning-v3.php b/content-warning-v3.php new file mode 100644 index 0000000..ebcdd6d --- /dev/null +++ b/content-warning-v3.php @@ -0,0 +1,18 @@ +uninstall() ); + +?> \ No newline at end of file diff --git a/css/admin_style.css b/css/admin_style.css new file mode 100644 index 0000000..de1504e --- /dev/null +++ b/css/admin_style.css @@ -0,0 +1,2 @@ +/* CSS Document */ + diff --git a/css/cwv3.css b/css/cwv3.css new file mode 100644 index 0000000..4969298 --- /dev/null +++ b/css/cwv3.css @@ -0,0 +1,15 @@ +/* CSS Document */ +.cwv3_box{background: transparent;} + +/* Undocumented cBox colors */ +#cboxLoadingOverlay, #cboxContent, #cboxLoadedContent{background-color: #FFF !important;} + +#cwv3_auth{border: 3px solid #ccc; background: #FFF;} +#cwv3_auth div{padding: 0.25em} +#cwv3_title{color: #FFF; font-weight: bold; text-align:center; background: #f00;} +#cwv3_btns{overflow:hidden;} +#cwv3_btns div a{ display: block; width: 100%; text-align:center; color: #FFF; font-weight:bold; text-decoration:none;} +#cwv3_enter{float:left; width: 40%;} +#cwv3_enter a{background-color: #0C3;} +#cwv3_exit{float:right; width: 40%;} +#cwv3_exit a{background-color: #F00;} \ No newline at end of file diff --git a/css/cwv3_admin.css b/css/cwv3_admin.css new file mode 100644 index 0000000..de1504e --- /dev/null +++ b/css/cwv3_admin.css @@ -0,0 +1,2 @@ +/* CSS Document */ + diff --git a/inc/options.inc.php b/inc/options.inc.php new file mode 100644 index 0000000..4595ab9 --- /dev/null +++ b/inc/options.inc.php @@ -0,0 +1,154 @@ +term_id; + $termName = $cw_cat->name; + + $final_cat_list[$termID] = $termName; +} +$cwv3_op_data = array( + 'plugin_title' => 'Content Warning v3', + 'prefix' => 'cwv3_', + 'menu_title' => 'CWv3 Options', + 'slug' => 'cwv3_options', + 'opData' => array( + 'sitewide' => array( + 'name' => 'Sitewide', + 'type' => 'check', + 'desc' => 'Takes priority over category, page, and post, home, and misc. pages/posts.', + 'fields' => array( + 'enabled' => 'Enable' + ), + 'def' => 'enabled' + ), + 'homepage' => array( + 'name' => 'Home Page', + 'type' => 'check', + 'desc' => 'Toggle the home page dialog, useful if you have not set a static page for your front-page in Settings -> Reading.', + 'fields' => array( + 'enabled' => 'Enable' + ), + 'def' => 'enabled' + + ), + 'misc' => array( + 'name' => 'Misc. Pages', + 'type' => 'check', + 'desc' => 'Enable this to protect search, archive, and other such pages.', + 'fields' => array( + 'enabled' => 'Enable' + ), + 'def' => 'enabled' + + ), + 'death' => array( + 'name' => 'Cookie Life', + 'desc' => 'How long before the cookie expires.', + 'type' => 'timeframe', + 'def' => array('multiplier'=>1, 'time'=>60*60*24) + ), + // Dialog Options + 'd_title' => array( + 'name' => 'Dialog Title', + 'desc' => '', + 'type' => 'text', + 'def' => 'WARNING: Explicit Content' + ), + 'd_msg' => array( + 'name' => 'Dialog Message', + 'type' => 'editor', + 'desc' => 'A message shown to your visitor.', + 'def' => 'The content you are about to view may be considered offensive and/or inappropriate. Furthermore, this content may be considered adult content, if you are not of legal age or are easily offended, you are required to click the exit button.', + 'settings' => array( + 'teeny' => true, + 'media_buttons' => false + ) + ), + 'exit_txt' => array( + 'name' => 'Exit Text', + 'type' => 'text', + 'desc' => 'The text for the exit button.', + 'def' => 'Exit' + ), + 'exit_link' => array( + 'name' => 'Exit Link', + 'type' => 'text', + 'desc' => 'The full URL a user should be directed to upon clicking the exit button.', + 'def' => 'http://google.com' + ), + 'enter_txt' => array( + 'name' => 'Enter Text', + 'type' => 'text', + 'desc' => 'The text for the enter button.', + 'def' => 'Enter' + ), + 'enter_link' => array( + 'name' => 'Enter Link', + 'type' => 'text', + 'desc' => 'The full URL a user should be directed to upon clicking the enter button. Leave blank to just close the dialog.', + 'def' => '#' + ), + // Denial Options + 'denial' => array( + 'name' => 'Toggle Denial Option', + 'desc' => '', + 'type' => 'check', + 'fields' => array('enabled' => 'Enable denial handling.') + ), + 'method' => array( + 'name' => 'Denial Handling Method', + 'desc' => '', + 'type' => 'radio', + 'fields' => array( + 'redirect' => 'Redirect the user.', + 'show' => 'Show the denial dialog.' + ), + 'def' => 'redirect' + ), + 'den_title' => array( + 'name' => 'Dialog Title', + 'desc' => '', + 'type' => 'text', + 'def' => 'Access Denied' + ), + 'den_msg' => array( + 'name' => 'Denial Message', + 'desc' => '', + 'type' => 'editor', + 'def' => 'You have been denied access to this content. If you feel this is in error, please contact a site administrator.', + 'settings' => array( + 'media_buttons' => false, + 'teeny' => true + ) + ), + // Advanced Options + //// Styling Options + 'bg_image' => array( + 'name' => 'Background Image', + 'desc' => 'If not empty, the dialog will use this instead of the background opacity and color.', + 'type' => 'media' + ), + 'bg_opacity' => array( + 'name' => 'Background Opacity', + 'desc' => 'Input a number from 0-100, the latter being completely opaque.', + 'type' => 'text', + 'def' => 75 + ), + 'bg_color' => array( + 'name' => 'Background Color', + 'desc' => 'The Overlay color.', + 'type' => 'color', + 'fields' => array('color'=>'#000000') + ), + 'cat_list' => array( + 'name' => 'Category restrictions', + 'desc' => 'Select categories that you would like to restrict with the dialog.', + 'type' => 'check', + 'fields' => $final_cat_list + ) + ) +); +?> \ No newline at end of file diff --git a/js/admin_script.js b/js/admin_script.js new file mode 100644 index 0000000..29180ed --- /dev/null +++ b/js/admin_script.js @@ -0,0 +1 @@ +// JavaScript Document \ No newline at end of file diff --git a/js/colorbox.1.4.14/colorbox.css b/js/colorbox.1.4.14/colorbox.css new file mode 100644 index 0000000..b463110 --- /dev/null +++ b/js/colorbox.1.4.14/colorbox.css @@ -0,0 +1,49 @@ +/* + Colorbox Core Style: + The following CSS is consistent between example themes and should not be altered. +*/ +#colorbox, #cboxOverlay, #cboxWrapper{position:absolute; top:0; left:0; z-index:9999; overflow:hidden;} +#cboxOverlay{position:fixed; width:100%; height:100%;} +#cboxMiddleLeft, #cboxBottomLeft{clear:left;} +#cboxContent{position:relative;} +#cboxLoadedContent{overflow:auto; -webkit-overflow-scrolling: touch;} +#cboxTitle{margin:0;} +#cboxLoadingOverlay, #cboxLoadingGraphic{position:absolute; top:0; left:0; width:100%; height:100%;} +#cboxPrevious, #cboxNext, #cboxClose, #cboxSlideshow{cursor:pointer;} +.cboxPhoto{float:left; margin:auto; border:0; display:block; max-width:none; -ms-interpolation-mode:bicubic;} +.cboxIframe{width:100%; height:100%; display:block; border:0;} +#colorbox, #cboxContent, #cboxLoadedContent{box-sizing:content-box; -moz-box-sizing:content-box; -webkit-box-sizing:content-box;} + +/* + User Style: + Change the following styles to modify the appearance of Colorbox. They are + ordered & tabbed in a way that represents the nesting of the generated HTML. +*/ +#cboxOverlay{background:#fff;} +#colorbox{outline:0;} + #cboxContent{margin-top:32px; overflow:visible; background:#000;} + .cboxIframe{background:#fff;} + #cboxError{padding:50px; border:1px solid #ccc;} + #cboxLoadedContent{background:#000; padding:1px;} + #cboxLoadingGraphic{background:url(images/loading.gif) no-repeat center center;} + #cboxLoadingOverlay{background:#000;} + #cboxTitle{position:absolute; top:-22px; left:0; color:#000;} + #cboxCurrent{position:absolute; top:-22px; right:205px; text-indent:-9999px;} + + /* these elements are buttons, and may need to have additional styles reset to avoid unwanted base styles */ + #cboxPrevious, #cboxNext, #cboxSlideshow, #cboxClose {border:0; padding:0; margin:0; overflow:visible; text-indent:-9999px; width:20px; height:20px; position:absolute; top:-20px; background:url(images/controls.png) no-repeat 0 0;} + + /* avoid outlines on :active (mouseclick), but preserve outlines on :focus (tabbed navigating) */ + #cboxPrevious:active, #cboxNext:active, #cboxSlideshow:active, #cboxClose:active {outline:0;} + + #cboxPrevious{background-position:0px 0px; right:44px;} + #cboxPrevious:hover{background-position:0px -25px;} + #cboxNext{background-position:-25px 0px; right:22px;} + #cboxNext:hover{background-position:-25px -25px;} + #cboxClose{background-position:-50px 0px; right:0;} + #cboxClose:hover{background-position:-50px -25px;} + .cboxSlideshow_on #cboxPrevious, .cboxSlideshow_off #cboxPrevious{right:66px;} + .cboxSlideshow_on #cboxSlideshow{background-position:-75px -25px; right:44px;} + .cboxSlideshow_on #cboxSlideshow:hover{background-position:-100px -25px;} + .cboxSlideshow_off #cboxSlideshow{background-position:-100px 0px; right:44px;} + .cboxSlideshow_off #cboxSlideshow:hover{background-position:-75px -25px;} diff --git a/js/colorbox.1.4.14/images/controls.png b/js/colorbox.1.4.14/images/controls.png new file mode 100644 index 0000000..8569b57 Binary files /dev/null and b/js/colorbox.1.4.14/images/controls.png differ diff --git a/js/colorbox.1.4.14/images/loading.gif b/js/colorbox.1.4.14/images/loading.gif new file mode 100644 index 0000000..19c67bb Binary files /dev/null and b/js/colorbox.1.4.14/images/loading.gif differ diff --git a/js/colorbox.1.4.14/jquery.colorbox-min.js b/js/colorbox.1.4.14/jquery.colorbox-min.js new file mode 100644 index 0000000..d6186e6 --- /dev/null +++ b/js/colorbox.1.4.14/jquery.colorbox-min.js @@ -0,0 +1,6 @@ +/*! + jQuery Colorbox v1.4.14 - 2013-04-16 + (c) 2013 Jack Moore - jacklmoore.com/colorbox + license: http://www.opensource.org/licenses/mit-license.php +*/ +(function(t,e,i){function o(i,o,n){var r=e.createElement(i);return o&&(r.id=te+o),n&&(r.style.cssText=n),t(r)}function n(){return i.innerHeight?i.innerHeight:t(i).height()}function r(t){var e=H.length,i=(j+t)%e;return 0>i?e+i:i}function h(t,e){return Math.round((/%/.test(t)?("x"===e?E.width():n())/100:1)*parseInt(t,10))}function l(t,e){return t.photo||t.photoRegex.test(e)}function s(t,e){return t.retinaUrl&&i.devicePixelRatio>1?e.replace(t.photoRegex,t.retinaSuffix):e}function a(t){"contains"in x[0]&&!x[0].contains(t.target)&&(t.stopPropagation(),x.focus())}function d(){var e,i=t.data(A,Z);null==i?(_=t.extend({},Y),console&&console.log&&console.log("Error: cboxElement missing settings object")):_=t.extend({},i);for(e in _)t.isFunction(_[e])&&"on"!==e.slice(0,2)&&(_[e]=_[e].call(A));_.rel=_.rel||A.rel||t(A).data("rel")||"nofollow",_.href=_.href||t(A).attr("href"),_.title=_.title||A.title,"string"==typeof _.href&&(_.href=t.trim(_.href))}function c(i,o){t(e).trigger(i),se.trigger(i),t.isFunction(o)&&o.call(A)}function u(){var t,e,i,o,n,r=te+"Slideshow_",h="click."+te;_.slideshow&&H[1]?(e=function(){clearTimeout(t)},i=function(){(_.loop||H[j+1])&&(t=setTimeout(J.next,_.slideshowSpeed))},o=function(){M.html(_.slideshowStop).unbind(h).one(h,n),se.bind(ne,i).bind(oe,e).bind(re,n),x.removeClass(r+"off").addClass(r+"on")},n=function(){e(),se.unbind(ne,i).unbind(oe,e).unbind(re,n),M.html(_.slideshowStart).unbind(h).one(h,function(){J.next(),o()}),x.removeClass(r+"on").addClass(r+"off")},_.slideshowAuto?o():n()):x.removeClass(r+"off "+r+"on")}function f(i){G||(A=i,d(),H=t(A),j=0,"nofollow"!==_.rel&&(H=t("."+ee).filter(function(){var e,i=t.data(this,Z);return i&&(e=t(this).data("rel")||i.rel||this.rel),e===_.rel}),j=H.index(A),-1===j&&(H=H.add(A),j=H.length-1)),g.css({opacity:parseFloat(_.opacity),cursor:_.overlayClose?"pointer":"auto",visibility:"visible"}).show(),V&&x.add(g).removeClass(V),_.className&&x.add(g).addClass(_.className),V=_.className,K.html(_.close).show(),$||($=q=!0,x.css({visibility:"hidden",display:"block"}),W=o(ae,"LoadedContent","width:0; height:0; overflow:hidden").appendTo(v),D=b.height()+k.height()+v.outerHeight(!0)-v.height(),B=C.width()+T.width()+v.outerWidth(!0)-v.width(),N=W.outerHeight(!0),z=W.outerWidth(!0),_.w=h(_.initialWidth,"x"),_.h=h(_.initialHeight,"y"),J.position(),u(),c(ie,_.onOpen),O.add(F).hide(),x.focus(),e.addEventListener&&(e.addEventListener("focus",a,!0),se.one(he,function(){e.removeEventListener("focus",a,!0)})),_.returnFocus&&se.one(he,function(){t(A).focus()})),w())}function p(){!x&&e.body&&(X=!1,E=t(i),x=o(ae).attr({id:Z,"class":t.support.opacity===!1?te+"IE":"",role:"dialog",tabindex:"-1"}).hide(),g=o(ae,"Overlay").hide(),S=o(ae,"LoadingOverlay").add(o(ae,"LoadingGraphic")),y=o(ae,"Wrapper"),v=o(ae,"Content").append(F=o(ae,"Title"),I=o(ae,"Current"),P=t('", + "", + "", + "" + ].join(""); + })(); + + function paletteTemplate (p, color, className) { + var html = []; + for (var i = 0; i < p.length; i++) { + var tiny = tinycolor(p[i]); + var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light"; + c += (tinycolor.equals(color, p[i])) ? " sp-thumb-active" : ""; + + var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter(); + html.push(''); + } + return "
" + html.join('') + "
"; + } + + function hideAll() { + for (var i = 0; i < spectrums.length; i++) { + if (spectrums[i]) { + spectrums[i].hide(); + } + } + } + + function instanceOptions(o, callbackContext) { + var opts = $.extend({}, defaultOpts, o); + opts.callbacks = { + 'move': bind(opts.move, callbackContext), + 'change': bind(opts.change, callbackContext), + 'show': bind(opts.show, callbackContext), + 'hide': bind(opts.hide, callbackContext), + 'beforeShow': bind(opts.beforeShow, callbackContext) + }; + + return opts; + } + + function spectrum(element, o) { + + var opts = instanceOptions(o, element), + flat = opts.flat, + showSelectionPalette = opts.showSelectionPalette, + localStorageKey = opts.localStorageKey, + theme = opts.theme, + callbacks = opts.callbacks, + resize = throttle(reflow, 10), + visible = false, + dragWidth = 0, + dragHeight = 0, + dragHelperHeight = 0, + slideHeight = 0, + slideWidth = 0, + alphaWidth = 0, + alphaSlideHelperWidth = 0, + slideHelperHeight = 0, + currentHue = 0, + currentSaturation = 0, + currentValue = 0, + currentAlpha = 1, + palette = opts.palette.slice(0), + paletteArray = $.isArray(palette[0]) ? palette : [palette], + selectionPalette = opts.selectionPalette.slice(0), + draggingClass = "sp-dragging"; + + var doc = element.ownerDocument, + body = doc.body, + boundElement = $(element), + disabled = false, + container = $(markup, doc).addClass(theme), + dragger = container.find(".sp-color"), + dragHelper = container.find(".sp-dragger"), + slider = container.find(".sp-hue"), + slideHelper = container.find(".sp-slider"), + alphaSliderInner = container.find(".sp-alpha-inner"), + alphaSlider = container.find(".sp-alpha"), + alphaSlideHelper = container.find(".sp-alpha-handle"), + textInput = container.find(".sp-input"), + paletteContainer = container.find(".sp-palette"), + initialColorContainer = container.find(".sp-initial"), + cancelButton = container.find(".sp-cancel"), + chooseButton = container.find(".sp-choose"), + isInput = boundElement.is("input"), + shouldReplace = isInput && !flat, + replacer = (shouldReplace) ? $(replaceInput).addClass(theme) : $([]), + offsetElement = (shouldReplace) ? replacer : boundElement, + previewElement = replacer.find(".sp-preview-inner"), + initialColor = opts.color || (isInput && boundElement.val()), + colorOnShow = false, + preferredFormat = opts.preferredFormat, + currentPreferredFormat = preferredFormat, + clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange; + + + function applyOptions() { + + container.toggleClass("sp-flat", flat); + container.toggleClass("sp-input-disabled", !opts.showInput); + container.toggleClass("sp-alpha-enabled", opts.showAlpha); + container.toggleClass("sp-buttons-disabled", !opts.showButtons || flat); + container.toggleClass("sp-palette-disabled", !opts.showPalette); + container.toggleClass("sp-palette-only", opts.showPaletteOnly); + container.toggleClass("sp-initial-disabled", !opts.showInitial); + container.addClass(opts.className); + + reflow(); + } + + function initialize() { + + if (IE) { + container.find("*:not(input)").attr("unselectable", "on"); + } + + applyOptions(); + + if (shouldReplace) { + boundElement.hide().after(replacer); + } + + if (flat) { + boundElement.after(container).hide(); + } + else { + $(body).append(container.hide()); + } + + if (localStorageKey && window.localStorage) { + + // Migrate old palettes over to new format. May want to remove this eventually. + try { + var oldPalette = window.localStorage[localStorageKey].split(",#"); + if (oldPalette.length > 1) { + delete window.localStorage[localStorageKey]; + $.each(oldPalette, function(i, c) { + addColorToSelectionPalette(c); + }); + } + } + catch(e) { } + + try { + selectionPalette = window.localStorage[localStorageKey].split(";"); + } + catch (e) { } + } + + offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { + if (!disabled) { + toggle(); + } + + e.stopPropagation(); + + if (!$(e.target).is("input")) { + e.preventDefault(); + } + }); + + if(boundElement.is(":disabled") || (opts.disabled === true)) { + disable(); + } + + // Prevent clicks from bubbling up to document. This would cause it to be hidden. + container.click(stopPropagation); + + // Handle user typed input + textInput.change(setFromTextInput); + textInput.bind("paste", function () { + setTimeout(setFromTextInput, 1); + }); + textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); + + cancelButton.text(opts.cancelText); + cancelButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + hide("cancel"); + }); + + chooseButton.text(opts.chooseText); + chooseButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + + if (isValid()) { + updateOriginalInput(true); + hide(); + } + }); + + draggable(alphaSlider, function (dragX, dragY, e) { + currentAlpha = (dragX / alphaWidth); + if (e.shiftKey) { + currentAlpha = Math.round(currentAlpha * 10) / 10; + } + + move(); + }); + + draggable(slider, function (dragX, dragY) { + currentHue = parseFloat(dragY / slideHeight); + move(); + }, dragStart, dragStop); + + draggable(dragger, function (dragX, dragY) { + currentSaturation = parseFloat(dragX / dragWidth); + currentValue = parseFloat((dragHeight - dragY) / dragHeight); + move(); + }, dragStart, dragStop); + + if (!!initialColor) { + set(initialColor); + + // In case color was black - update the preview UI and set the format + // since the set function will not run (default color is black). + updateUI(); + currentPreferredFormat = preferredFormat || tinycolor(initialColor).format; + + addColorToSelectionPalette(initialColor); + + } + else { + updateUI(); + } + + if (flat) { + show(); + } + + function palletElementClick(e) { + if (e.data && e.data.ignore) { + set($(this).data("color")); + move(); + } + else { + set($(this).data("color")); + updateOriginalInput(true); + move(); + hide(); + } + + return false; + } + + var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; + paletteContainer.delegate(".sp-thumb-el", paletteEvent, palletElementClick); + initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, palletElementClick); + } + + function addColorToSelectionPalette(color) { + if (showSelectionPalette) { + var colorRgb = tinycolor(color).toRgbString(); + if ($.inArray(colorRgb, selectionPalette) === -1) { + selectionPalette.push(colorRgb); + } + + if (localStorageKey && window.localStorage) { + try { + window.localStorage[localStorageKey] = selectionPalette.join(";"); + } + catch(e) { } + } + } + } + + function getUniqueSelectionPalette() { + var unique = []; + var p = selectionPalette; + var paletteLookup = {}; + var rgb; + + if (opts.showPalette) { + + for (var i = 0; i < paletteArray.length; i++) { + for (var j = 0; j < paletteArray[i].length; j++) { + rgb = tinycolor(paletteArray[i][j]).toRgbString(); + paletteLookup[rgb] = true; + } + } + + for (i = 0; i < p.length; i++) { + rgb = tinycolor(p[i]).toRgbString(); + + if (!paletteLookup.hasOwnProperty(rgb)) { + unique.push(p[i]); + paletteLookup[rgb] = true; + } + } + } + + return unique.reverse().slice(0, opts.maxSelectionSize); + } + + function drawPalette() { + + var currentColor = get(); + + var html = $.map(paletteArray, function (palette, i) { + return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i); + }); + + if (selectionPalette) { + html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection")); + } + + paletteContainer.html(html.join("")); + } + + function drawInitial() { + if (opts.showInitial) { + var initial = colorOnShow; + var current = get(); + initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial")); + } + } + + function dragStart() { + if (dragHeight === 0 || dragWidth === 0 || slideHeight === 0) { + reflow(); + } + container.addClass(draggingClass); + } + + function dragStop() { + container.removeClass(draggingClass); + } + + function setFromTextInput() { + var tiny = tinycolor(textInput.val()); + if (tiny.ok) { + set(tiny); + } + else { + textInput.addClass("sp-validation-error"); + } + } + + function toggle() { + if (visible) { + hide(); + } + else { + show(); + } + } + + function show() { + if (visible) { + reflow(); + return; + } + if (callbacks.beforeShow(get()) === false) return; + + hideAll(); + visible = true; + + $(doc).bind("click.spectrum", hide); + $(window).bind("resize.spectrum", resize); + replacer.addClass("sp-active"); + container.show(); + + if (opts.showPalette) { + drawPalette(); + } + reflow(); + updateUI(); + + colorOnShow = get(); + + drawInitial(); + callbacks.show(colorOnShow); + } + + function hide(e) { + + // Return on right click + if (e && e.type == "click" && e.button == 2) { return; } + + // Return if hiding is unnecessary + if (!visible || flat) { return; } + visible = false; + + $(doc).unbind("click.spectrum", hide); + $(window).unbind("resize.spectrum", resize); + + replacer.removeClass("sp-active"); + container.hide(); + + var colorHasChanged = !tinycolor.equals(get(), colorOnShow); + + if (colorHasChanged) { + if (clickoutFiresChange && e !== "cancel") { + updateOriginalInput(true); + } + else { + revert(); + } + } + + callbacks.hide(get()); + } + + function revert() { + set(colorOnShow, true); + } + + function set(color, ignoreFormatChange) { + if (tinycolor.equals(color, get())) { + return; + } + + var newColor = tinycolor(color); + var newHsv = newColor.toHsv(); + + currentHue = newHsv.h; + currentSaturation = newHsv.s; + currentValue = newHsv.v; + currentAlpha = newHsv.a; + + updateUI(); + + if (!ignoreFormatChange) { + currentPreferredFormat = preferredFormat || newColor.format; + } + } + + function get() { + return tinycolor.fromRatio({ h: currentHue, s: currentSaturation, v: currentValue, a: Math.round(currentAlpha * 100) / 100 }); + } + + function isValid() { + return !textInput.hasClass("sp-validation-error"); + } + + function move() { + updateUI(); + + callbacks.move(get()); + } + + function updateUI() { + + textInput.removeClass("sp-validation-error"); + + updateHelperLocations(); + + // Update dragger background color (gradients take care of saturation and value). + var flatColor = tinycolor({ h: currentHue, s: "1.0", v: "1.0" }); + dragger.css("background-color", flatColor.toHexString()); + + // Get a format that alpha will be included in (hex and names ignore alpha) + var format = currentPreferredFormat; + if (currentAlpha < 1) { + if (format === "hex" || format === "name") { + format = "rgb"; + } + } + + var realColor = get(), + realHex = realColor.toHexString(), + realRgb = realColor.toRgbString(); + + + // Update the replaced elements background color (with actual selected color) + if (rgbaSupport || realColor.alpha === 1) { + previewElement.css("background-color", realRgb); + } + else { + previewElement.css("background-color", "transparent"); + previewElement.css("filter", realColor.toFilter()); + } + + if (opts.showAlpha) { + var rgb = realColor.toRgb(); + rgb.a = 0; + var realAlpha = tinycolor(rgb).toRgbString(); + var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")"; + + if (IE) { + alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex)); + } + else { + alphaSliderInner.css("background", "-webkit-" + gradient); + alphaSliderInner.css("background", "-moz-" + gradient); + alphaSliderInner.css("background", "-ms-" + gradient); + alphaSliderInner.css("background", gradient); + } + } + + + // Update the text entry input as it changes happen + if (opts.showInput) { + if (currentAlpha < 1) { + if (format === "hex" || format === "name") { + format = "rgb"; + } + } + textInput.val(realColor.toString(format)); + } + + if (opts.showPalette) { + drawPalette(); + } + + drawInitial(); + } + + function updateHelperLocations() { + var s = currentSaturation; + var v = currentValue; + + // Where to show the little circle in that displays your current selected color + var dragX = s * dragWidth; + var dragY = dragHeight - (v * dragHeight); + dragX = Math.max( + -dragHelperHeight, + Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight) + ); + dragY = Math.max( + -dragHelperHeight, + Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight) + ); + dragHelper.css({ + "top": dragY, + "left": dragX + }); + + var alphaX = currentAlpha * alphaWidth; + alphaSlideHelper.css({ + "left": alphaX - (alphaSlideHelperWidth / 2) + }); + + // Where to show the bar that displays your current selected hue + var slideY = (currentHue) * slideHeight; + slideHelper.css({ + "top": slideY - slideHelperHeight + }); + } + + function updateOriginalInput(fireCallback) { + var color = get(); + + if (isInput) { + boundElement.val(color.toString(currentPreferredFormat)).change(); + } + + var hasChanged = !tinycolor.equals(color, colorOnShow); + colorOnShow = color; + + // Update the selection palette with the current color + addColorToSelectionPalette(color); + if (fireCallback && hasChanged) { + callbacks.change(color); + } + } + + function reflow() { + dragWidth = dragger.width(); + dragHeight = dragger.height(); + dragHelperHeight = dragHelper.height(); + slideWidth = slider.width(); + slideHeight = slider.height(); + slideHelperHeight = slideHelper.height(); + alphaWidth = alphaSlider.width(); + alphaSlideHelperWidth = alphaSlideHelper.width(); + + if (!flat) { + container.offset(getOffset(container, offsetElement)); + } + + updateHelperLocations(); + } + + function destroy() { + boundElement.show(); + offsetElement.unbind("click.spectrum touchstart.spectrum"); + container.remove(); + replacer.remove(); + spectrums[spect.id] = null; + } + + function option(optionName, optionValue) { + if (optionName === undefined) { + return $.extend({}, opts); + } + if (optionValue === undefined) { + return opts[optionName]; + } + + opts[optionName] = optionValue; + applyOptions(); + } + + function enable() { + disabled = false; + boundElement.attr("disabled", false); + offsetElement.removeClass("sp-disabled"); + } + + function disable() { + hide(); + disabled = true; + boundElement.attr("disabled", true); + offsetElement.addClass("sp-disabled"); + } + + initialize(); + + var spect = { + show: show, + hide: hide, + toggle: toggle, + reflow: reflow, + option: option, + enable: enable, + disable: disable, + set: function (c) { + set(c); + updateOriginalInput(); + }, + get: get, + destroy: destroy, + container: container + }; + + spect.id = spectrums.push(spect) - 1; + + return spect; + } + + /** + * checkOffset - get the offset below/above and left/right element depending on screen position + * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js + */ + function getOffset(picker, input) { + var extraY = 0; + var dpWidth = picker.outerWidth(); + var dpHeight = picker.outerHeight(); + var inputHeight = input.outerHeight(); + var doc = picker[0].ownerDocument; + var docElem = doc.documentElement; + var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); + var viewHeight = docElem.clientHeight + $(doc).scrollTop(); + var offset = input.offset(); + offset.top += inputHeight; + + offset.left -= + Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + + offset.top -= + Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight - extraY) : extraY)); + + return offset; + } + + /** + * noop - do nothing + */ + function noop() { + + } + + /** + * stopPropagation - makes the code only doing this a little easier to read in line + */ + function stopPropagation(e) { + e.stopPropagation(); + } + + /** + * Create a function bound to a given object + * Thanks to underscore.js + */ + function bind(func, obj) { + var slice = Array.prototype.slice; + var args = slice.call(arguments, 2); + return function () { + return func.apply(obj, args.concat(slice.call(arguments))); + }; + } + + /** + * Lightweight drag helper. Handles containment within the element, so that + * when dragging, the x is within [0,element.width] and y is within [0,element.height] + */ + function draggable(element, onmove, onstart, onstop) { + onmove = onmove || function () { }; + onstart = onstart || function () { }; + onstop = onstop || function () { }; + var doc = element.ownerDocument || document; + var dragging = false; + var offset = {}; + var maxHeight = 0; + var maxWidth = 0; + var hasTouch = ('ontouchstart' in window); + + var duringDragEvents = {}; + duringDragEvents["selectstart"] = prevent; + duringDragEvents["dragstart"] = prevent; + duringDragEvents[(hasTouch ? "touchmove" : "mousemove")] = move; + duringDragEvents[(hasTouch ? "touchend" : "mouseup")] = stop; + + function prevent(e) { + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.returnValue = false; + } + + function move(e) { + if (dragging) { + // Mouseup happened outside of window + if (IE && document.documentMode < 9 && !e.button) { + return stop(); + } + + var touches = e.originalEvent.touches; + var pageX = touches ? touches[0].pageX : e.pageX; + var pageY = touches ? touches[0].pageY : e.pageY; + + var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth)); + var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight)); + + if (hasTouch) { + // Stop scrolling in iOS + prevent(e); + } + + onmove.apply(element, [dragX, dragY, e]); + } + } + function start(e) { + var rightclick = (e.which) ? (e.which == 3) : (e.button == 2); + var touches = e.originalEvent.touches; + + if (!rightclick && !dragging) { + if (onstart.apply(element, arguments) !== false) { + dragging = true; + maxHeight = $(element).height(); + maxWidth = $(element).width(); + offset = $(element).offset(); + + $(doc).bind(duringDragEvents); + $(doc.body).addClass("sp-dragging"); + + if (!hasTouch) { + move(e); + } + + prevent(e); + } + } + } + function stop() { + if (dragging) { + $(doc).unbind(duringDragEvents); + $(doc.body).removeClass("sp-dragging"); + onstop.apply(element, arguments); + } + dragging = false; + } + + $(element).bind(hasTouch ? "touchstart" : "mousedown", start); + } + + function throttle(func, wait, debounce) { + var timeout; + return function () { + var context = this, args = arguments; + var throttler = function () { + timeout = null; + func.apply(context, args); + }; + if (debounce) clearTimeout(timeout); + if (debounce || !timeout) timeout = setTimeout(throttler, wait); + }; + } + + + /** + * Define a jQuery plugin + */ + var dataID = "spectrum.id"; + $.fn.spectrum = function (opts, extra) { + + if (typeof opts == "string") { + + var returnValue = this; + var args = Array.prototype.slice.call( arguments, 1 ); + + this.each(function () { + var spect = spectrums[$(this).data(dataID)]; + if (spect) { + + var method = spect[opts]; + if (!method) { + throw new Error( "Spectrum: no such method: '" + opts + "'" ); + } + + if (opts == "get") { + returnValue = spect.get(); + } + else if (opts == "container") { + returnValue = spect.container; + } + else if (opts == "option") { + returnValue = spect.option.apply(spect, args); + } + else if (opts == "destroy") { + spect.destroy(); + $(this).removeData(dataID); + } + else { + method.apply(spect, args); + } + } + }); + + return returnValue; + } + + // Initializing a new instance of spectrum + return this.spectrum("destroy").each(function () { + var spect = spectrum(this, opts); + $(this).data(dataID, spect.id); + }); + }; + + $.fn.spectrum.load = true; + $.fn.spectrum.loadOpts = {}; + $.fn.spectrum.draggable = draggable; + $.fn.spectrum.defaults = defaultOpts; + + $.spectrum = { }; + $.spectrum.localization = { }; + $.spectrum.palettes = { }; + + $.fn.spectrum.processNativeColorInputs = function () { + var colorInput = $("")[0]; + var supportsColor = colorInput.type === "color" && colorInput.value != "!"; + + if (!supportsColor) { + $("input[type=color]").spectrum({ + preferredFormat: "hex6" + }); + } + }; + + // TinyColor.js - - 2011 Brian Grinstead - v0.5 + + (function (window) { + + var trimLeft = /^[\s,#]+/, + trimRight = /\s+$/, + tinyCounter = 0, + math = Math, + mathRound = math.round, + mathMin = math.min, + mathMax = math.max, + mathRandom = math.random, + parseFloat = window.parseFloat; + + function tinycolor(color, opts) { + + // If input is already a tinycolor, return itself + if (typeof color == "object" && color.hasOwnProperty("_tc_id")) { + return color; + } + + var rgb = inputToRGB(color); + var r = rgb.r, g = rgb.g, b = rgb.b, a = parseFloat(rgb.a), format = rgb.format; + + return { + ok: rgb.ok, + format: format, + _tc_id: tinyCounter++, + alpha: a, + toHsv: function () { + var hsv = rgbToHsv(r, g, b); + return { h: hsv.h, s: hsv.s, v: hsv.v, a: a }; + }, + toHsvString: function () { + var hsv = rgbToHsv(r, g, b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, " + a + ")"; + }, + toHsl: function () { + var hsl = rgbToHsl(r, g, b); + return { h: hsl.h, s: hsl.s, l: hsl.l, a: a }; + }, + toHslString: function () { + var hsl = rgbToHsl(r, g, b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, " + a + ")"; + }, + toHex: function () { + return rgbToHex(r, g, b); + }, + toHexString: function (force6Char) { + return '#' + rgbToHex(r, g, b, force6Char); + }, + toRgb: function () { + return { r: mathRound(r), g: mathRound(g), b: mathRound(b), a: a }; + }, + toRgbString: function () { + return (a == 1) ? + "rgb(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ")" : + "rgba(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ", " + a + ")"; + }, + toName: function () { + return hexNames[rgbToHex(r, g, b)] || false; + }, + toFilter: function (opts, secondColor) { + + var hex = rgbToHex(r, g, b, true); + var secondHex = hex; + var alphaHex = Math.round(parseFloat(a) * 255).toString(16); + var secondAlphaHex = alphaHex; + var gradientType = opts && opts.gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex = s.toHex(); + secondAlphaHex = Math.round(parseFloat(s.alpha) * 255).toString(16); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr=#" + pad2(alphaHex) + hex + ",endColorstr=#" + pad2(secondAlphaHex) + secondHex + ")"; + }, + toString: function (format) { + format = format || this.format; + var formattedString = false; + if (format === "rgb") { + formattedString = this.toRgbString(); + } + if (format === "hex") { + formattedString = this.toHexString(); + } + if (format === "hex6") { + formattedString = this.toHexString(true); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + return formattedString || this.toHexString(true); + } + }; + } + + // If input is an object, force 1 into "1.0" to handle ratios properly + // String input requires "1.0" as input, so 1 will be treated as 1 + tinycolor.fromRatio = function (color) { + + if (typeof color == "object") { + for (var i in color) { + if (color[i] === 1) { + color[i] = "1.0"; + } + } + } + + return tinycolor(color); + + }; + + // Given a string or object, convert that input to RGB + // Possible string inputs: + // + // "red" + // "#f00" or "f00" + // "#ff0000" or "ff0000" + // "rgb 255 0 0" or "rgb (255, 0, 0)" + // "rgb 1.0 0 0" or "rgb (1, 0, 0)" + // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" + // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" + // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" + // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" + // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" + // + function inputToRGB(color) { + + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = "rgb"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { + rgb = hsvToRgb(color.h, color.s, color.v); + ok = true; + format = "hsv"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { + rgb = hslToRgb(color.h, color.s, color.l); + ok = true; + format = "hsl"; + } + + if (color.hasOwnProperty("a")) { + a = color.a; + } + } + + rgb.r = mathMin(255, mathMax(rgb.r, 0)); + rgb.g = mathMin(255, mathMax(rgb.g, 0)); + rgb.b = mathMin(255, mathMax(rgb.b, 0)); + + + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1. + // If it was supposed to be 128, this was already taken care of in the conversion function + if (rgb.r < 1) { rgb.r = mathRound(rgb.r); } + if (rgb.g < 1) { rgb.g = mathRound(rgb.g); } + if (rgb.b < 1) { rgb.b = mathRound(rgb.b); } + + return { + ok: ok, + format: (color && color.format) || format, + r: rgb.r, + g: rgb.g, + b: rgb.b, + a: a + }; + } + + + + // Conversion Functions + // -------------------- + + // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: + // + + // `rgbToRgb` + // Handle bounds / percentage checking to conform to CSS color spec + // + // *Assumes:* r, g, b in [0, 255] or [0, 1] + // *Returns:* { r, g, b } in [0, 255] + function rgbToRgb(r, g, b) { + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; + } + + // `rgbToHsl` + // Converts an RGB color value to HSL. + // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] + // *Returns:* { h, s, l } in [0,1] + function rgbToHsl(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; + + if (max == min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + return { h: h, s: s, l: l }; + } + + // `hslToRgb` + // Converts an HSL color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hslToRgb(h, s, l) { + var r, g, b; + + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); + + function hue2rgb(p, q, t) { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + } + + if (s === 0) { + r = g = b = l; // achromatic + } + else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHsv` + // Converts an RGB color value to HSV + // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] + // *Returns:* { h, s, v } in [0,1] + function rgbToHsv(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if (max == min) { + h = 0; // achromatic + } + else { + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; + } + + // `hsvToRgb` + // Converts an HSV color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) { + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHex` + // Converts an RGB color to hex + // Assumes r, g, and b are contained in the set [0, 255] + // Returns a 3 or 6 character hex + function rgbToHex(r, g, b, force6Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + // Return a 3 character hex if possible + if (!force6Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } + + return hex.join(""); + } + + // `equals` + // Can be called with any tinycolor input + tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { return false; } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); + }; + tinycolor.random = function () { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); + }; + + + // Modification Functions + // ---------------------- + // Thanks to less.js for some of the basics here + // + + + tinycolor.desaturate = function (color, amount) { + var hsl = tinycolor(color).toHsl(); + hsl.s -= ((amount || 10) / 100); + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + }; + tinycolor.saturate = function (color, amount) { + var hsl = tinycolor(color).toHsl(); + hsl.s += ((amount || 10) / 100); + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + }; + tinycolor.greyscale = function (color) { + return tinycolor.desaturate(color, 100); + }; + tinycolor.lighten = function (color, amount) { + var hsl = tinycolor(color).toHsl(); + hsl.l += ((amount || 10) / 100); + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + }; + tinycolor.darken = function (color, amount) { + var hsl = tinycolor(color).toHsl(); + hsl.l -= ((amount || 10) / 100); + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + }; + tinycolor.complement = function (color) { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 0.5) % 1; + return tinycolor(hsl); + }; + + + // Combination Functions + // --------------------- + // Thanks to jQuery xColor for some of the ideas behind these + // + + tinycolor.triad = function (color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h * 360; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; + }; + tinycolor.tetrad = function (color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h * 360; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; + }; + tinycolor.splitcomplement = function (color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h * 360; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l }) + ]; + }; + tinycolor.analogous = function (color, results, slices) { + results = results || 6; + slices = slices || 30; + + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; + + hsl.h *= 360; + + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; + }; + tinycolor.monochromatic = function (color, results) { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; + + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v })); + v = (v + modification) % 1; + } + + return ret; + }; + tinycolor.readable = function (color1, color2) { + var a = tinycolor(color1).toRgb(), b = tinycolor(color2).toRgb(); + return ( + (b.r - a.r) * (b.r - a.r) + + (b.g - a.g) * (b.g - a.g) + + (b.b - a.b) * (b.b - a.b) + ) > 0x28A4; + }; + + // Big List of Colors + // --------- + // + var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" + }; + + // Make it easy to access colors via `hexNames[hex]` + var hexNames = tinycolor.hexNames = flip(names); + + + // Utilities + // --------- + + // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` + function flip(o) { + var flipped = {}; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } + } + return flipped; + } + + // Take input from [0, n] and return it as [0, 1] + function bound01(n, max) { + if (isOnePointZero(n)) { n = "100%"; } + + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); + + // Automatically convert percentage into number + if (processPercent) { + n = n * (max / 100); + } + + // Handle floating point rounding errors + if (math.abs(n - max) < 0.000001) { + return 1; + } + else if (n >= 1) { + return (n % max) / parseFloat(max); + } + return n; + } + + // Force a number between 0 and 1 + function clamp01(val) { + return mathMin(1, mathMax(0, val)); + } + + // Parse an integer into hex + function parseHex(val) { + return parseInt(val, 16); + } + + // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 + // + function isOnePointZero(n) { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; + } + + // Check to see if string passed in is a percentage + function isPercentage(n) { + return typeof n === "string" && n.indexOf('%') != -1; + } + + // Force a hex value to have 2 characters + function pad2(c) { + return c.length == 1 ? '0' + c : '' + c; + } + + var matchers = (function () { + + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; + + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; + + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; + + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + + return { + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ + }; + })(); + + // `stringInputToObject` + // Permissive string parsing. Take in a number of formats, and output an object + // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` + function stringInputToObject(color) { + + color = color.replace(trimLeft, '').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } + else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0 }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseHex(match[1]), + g: parseHex(match[2]), + b: parseHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseHex(match[1] + '' + match[1]), + g: parseHex(match[2] + '' + match[2]), + b: parseHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; + } + + // Everything is ready, expose to window + window.tinycolor = tinycolor; + + })(this); + + $(function () { + if ($.fn.spectrum.load) { + $.fn.spectrum.processNativeColorInputs(); + } + }); + +})(window, jQuery); \ No newline at end of file diff --git a/lib/jw_simple_options/simple_options.php b/lib/jw_simple_options/simple_options.php new file mode 100644 index 0000000..e243589 --- /dev/null +++ b/lib/jw_simple_options/simple_options.php @@ -0,0 +1,504 @@ +__construct($ops); + } + + function __construct(array $ops){ + // Setup variables + $this->plugin_title = empty($ops['plugin_title']) ? $this->plugin_title : $ops['plugin_title']; + $this->menu_title = empty($ops['menu_title']) ? $this->menu_title : $ops['menu_title']; + $this->cap = empty($ops['capability']) ? $this->cap : $ops['capability']; + $this->slug = empty($ops['slug']) ? $this->prefix.$this->slug : $ops['slug']; + $this->options = empty($ops['opData']) ? $this->options : $ops['opData']; + $this->icon = empty($ops['icon_url']) ? $this->icon : $ops['icon_url']; + $this->pos = empty($ops['menu_pos']) ? $this->pos : $ops['menu_pos']; + $this->prefix = empty($ops['prefix']) ? $this->prefix : $ops['prefix']; + + add_action('admin_init', array(&$this, 'register_admin_deps') ); + add_action('admin_menu', array(&$this, 'load_admin_menu') ); + add_action('admin_enqueue_scripts', array(&$this, 'load_admin_deps') ); + + + } + + /** + * Builds an array of check boxes. + * + * @param string $key Option identifier minus prefix. + * @param array $data Associative array of data to display. + */ + public function buildCheckFields($key, $data, $def = false){ + $opData = get_option($this->prefix.$key, $def); + ?> +
+ $v): ?> + + +
+ prefix.$key); + ?> + [+] Add Row + + + + + + $v): ?> + + + + + + + + + $colLabel): ?> + + + + + + + + + $colLabel): ?> + + + + + +
[X]
[X]
+ prefix.$key); + + $output = '
'; + $output .= ''; + $output .= ''; + $output .= '
'; + + return $output; + } + + /** + * Builds an array of radio buttons. + * + * @param string $key Option identifier minus prefix. + * @param array $data Associative array of data to display. + * @param boolean $def If not false, provide a default value if no option exists. + */ + public function buildRadioFields($key, $data, $def = false){ + $opData = get_option($this->prefix.$key, $def); + ?> +
+ $v): ?> + + +
+ prefix.$key, $def); + + $output = ''; + $output .= ''; + + return $output; + + } + /** + * Builds a timeframe selection that consists of one text input, and one dropdown. + * + * @param string $key Option identifier minus prefix. + * @param mixed $def If not false, provide a default value if no option exists. + */ + public function buildTimeframe($key, $def = false){ + // Should be two fields, one input text, one dropdown. + $opData = get_option($this->prefix.$key, $def); + + if(empty($opData['multiplier'])) $opData['mulitplier'] = $def['multiplier']; + if(empty($opData['time'])) $opData['time'] = $def['time']; + + ?> + + menu_type){ + case 'page': + $hook = add_pages_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'link': + $hook = add_links_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'comment': + $hook = add_comments_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'management': + $hook = add_management_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'option': + $hook = add_options_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'theme': + $hook = add_theme_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'plugin': + $hook = add_plugins_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'user': + $hook = add_users_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'dashboard': + $hook = add_dashboard_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'post': + $hook = add_posts_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + case 'media': + $hook = add_media_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page')); + break; + default: + $hook = add_menu_page($this->plugin_title, $this->menu_title, $this->cap, $this->slug, array(&$this, 'render_options_page'), $this->icon, $this->menu_pos); + break; + } + $this->hook = $hook; + } + + /** + * Load up admin dependancies for functionality + */ + public function load_admin_deps($hook = false){ + if($hook == $this->hook && $hook != false){ + + if(function_exists('wp_enqueue_media')) wp_enqueue_media(); + + wp_enqueue_style('spectrum'); + wp_enqueue_script('spectrum'); + + wp_enqueue_style($this->prefix.'admin_css'); + wp_enqueue_script($this->prefix.'admin_js'); + } + } + + /** + * Registering Admin information. + */ + public function register_admin_deps(){ + foreach($this->options as $k => $v) register_setting($this->prefix.'options', $this->prefix.$k); + + + if(preg_match('/\/themes\//i', $this->file_data)){ + $type = 'theme'; + }elseif(preg_match('/\/plugins\//i', $this->file_data)){ + $type = 'plugin'; + } + + if('theme' == $type){ + $stylesheetDir = get_bloginfo('stylesheet_directory'); + $urls = array( + $stylesheetDir.'/'.basename( dirname( dirname(__FILE__) ) ).'/jw-simple-options/css', + $stylesheetDir.'/'.basename( dirname( dirname(__FILE__) ) ).'/jw-simple-options/js', + ); + }else{ + $urls = array( + 'css' => plugins_url('css', __FILE__), + 'js' => plugins_url('js', __FILE__) + ); + } + + wp_register_style('spectrum', $urls['css'].'/spectrum.css', '', '1.0.9'); + wp_register_script('spectrum', $urls['js'].'/spectrum.js', array('jquery'), '1.0.9' ); + + wp_register_style( $this->prefix.'admin_css', $urls['css'].'/jw_simple_options.css', '', '1.0'); + wp_register_script( $this->prefix.'admin_js', $urls['js'].'/jquery.jw_simple_options.js' , '', '1.0'); + + } + + /** + * Display user-end options page + */ + public function render_options_page(){ + + ?> +
+

+

plugin_title; ?>

+

Options page powered by: JW Simple Options

+
+ prefix.'options'); ?> + + + options as $k => $v){ + ?> + + + + + + +
render_option_field($k, $v); ?> + +

+ +
+

+ +

+
+
+ '#FFFFFF') + */ + public function render_color_select($key, $data){ + + $opData = get_option($this->prefix.$key, $data); + + $output = ''; + foreach($opData as $k => $v){ + $output .= ''; + } + + return $output; + + } + + /** + * Switch between options data types and display them. + * + * Offload rendering where necessary. + */ + public function render_option_field($key, $data){ + switch($data['type']){ + case 'text': + $output = ''; + break; + case 'password': + $output = ''; + break; + case 'number': + $output = ''; + break; + case 'data_array': + $output = $this->buildDataArrayFields($key, $data['fields'], $data['showhead']); + break; + case 'select': + $output = $this->buildSelectOptions($key, $data['fields'], $data['def']); + break; + case 'color': + $output = $this->render_color_select($key, $data['fields']); + break; + case 'media': + if(function_exists('wp_enqueue_media')) $output = $this->buildMediaOption($key); + break; + case 'check': + $output = $this->buildCheckFields($key, $data['fields'], $data['def']); + break; + case 'radio': + $output = $this->buildRadioFields($key, $data['fields'], $data['def']); + break; + case 'textbox': + $output = ''; + break; + case 'timeframe': + $output = $this->buildTimeframe($key, $data['def']); + break; + case 'editor': + $opData = get_option($this->prefix.$key, $data['def']); + $output = wp_editor($opData, $this->prefix.$key, $data['settings']); + default: + $output = ''; + break; + } + return $output; + } + + /** + * Uninstalls any options. + * + * Needs to be called from functions.php + * @see register_uninstall_hook() + */ + public function uninstall(){ + if( !defined( 'WP_UNINSTALL_PLUGIN' ) ) return false; + // Remove options + foreach ($this->options as $k => $v) delete_option($this->prefix.$k); + } + +} + +?> \ No newline at end of file