diff options
51 files changed, 1641 insertions, 143 deletions
diff --git a/include/datetime.php b/include/datetime.php index 59dad2045..346d03bd4 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -14,25 +14,17 @@ function timezone_cmp($a, $b) { return ( t($a) < t($b)) ? -1 : 1; } -// emit a timezone selector grouped (primarily) by continent - -function select_timezone($current = 'America/Los_Angeles') { - +// Return timezones grouped (primarily) by continent +function get_timezones( ){ $timezone_identifiers = DateTimeZone::listIdentifiers(); - - $o ='<select id="timezone_select" name="timezone">'; usort($timezone_identifiers, 'timezone_cmp'); $continent = ''; + $continents = array(); foreach($timezone_identifiers as $value) { $ex = explode("/", $value); if(count($ex) > 1) { - if($ex[0] != $continent) { - if($continent != '') - $o .= '</optgroup>'; - $continent = $ex[0]; - $o .= '<optgroup label="' . t($continent) . '">'; - } + $continent = t($ex[0]); if(count($ex) > 2) $city = substr($value,strpos($value,'/')+1); else @@ -40,35 +32,14 @@ function select_timezone($current = 'America/Los_Angeles') { } else { $city = $ex[0]; - if($continent != t('Miscellaneous')) { - $o .= '</optgroup>'; - $continent = t('Miscellaneous'); - $o .= '<optgroup label="' . t($continent) . '">'; - } + $continent = t('Miscellaneous'); } $city = str_replace('_', ' ', t($city)); - $selected = (($value == $current) ? " selected=\"selected\" " : ""); - $o .= "<option value=\"$value\" $selected >$city</option>"; - } - $o .= '</optgroup></select>'; - return $o; -} - -// return a select using 'field_select_raw' template, with timezones -// groupped (primarily) by continent -// arguments follow convetion as other field_* template array: -// 'name', 'label', $value, 'help' -function field_timezone($name='timezone', $label='', $current = 'America/Los_Angeles', $help){ - $options = select_timezone($current); - $options = str_replace('<select id="timezone_select" name="timezone">','', $options); - $options = str_replace('</select>','', $options); - - $tpl = get_markup_template('field_select_raw.tpl'); - return replace_macros($tpl, array( - '$field' => array($name, $label, $current, $help, $options), - )); - + if(!x($continents,$ex[0])) $continents[$ex[0]] = array(); + $continents[$continent][$value] = $city; + } + return $continents; } // General purpose date parse/convert function. diff --git a/include/permissions.php b/include/permissions.php index 9e60223fb..ccbde1a7c 100644 --- a/include/permissions.php +++ b/include/permissions.php @@ -800,38 +800,18 @@ function get_role_perms($role) { } /** - * @brief Creates a HTML select field with all available roles. + * @brief Returns a list or roles, grouped by type * * @param string $current The current role - * @return string Returns the complete HTML code for this privacy-role-select field. + * @return string Returns an array of roles, grouped by type */ -function role_selector($current) { - - if(! $current) - $current = 'custom'; - +function get_roles() { $roles = array( - 'social' => array( t('Social Networking'), - array('social' => t('Mostly Public'), 'social_restricted' => t('Restricted'), 'social_private' => t('Private'))), - 'forum' => array( t('Community Forum'), - array('forum' => t('Mostly Public'), 'forum_restricted' => t('Restricted'), 'forum_private' => t('Private'))), - 'feed' => array( t('Feed Republish'), - array('feed' => t('Mostly Public'), 'feed_restricted' => t('Restricted'))), - 'special' => array( t('Special Purpose'), - array('soapbox' => t('Celebrity/Soapbox'), 'repository' => t('Group Repository'))), - 'other' => array( t('Other'), - array('custom' => t('Custom/Expert Mode')))); - - $o = '<select name="permissions_role" id="privacy-role-select">'; - foreach($roles as $k => $v) { - $o .= '<optgroup label="'. htmlspecialchars($v[0]) . '">'; - foreach($v[1] as $kk => $vv) { - $selected = (($kk === $current) ? ' selected="selected"' : ''); - $o .= '<option value="' . $kk . '"' . $selected . '>' . htmlspecialchars($vv) . '</option>'; - } - $o .= '</optgroup>'; - } - $o .= '</select>'; + t('Social Networking') => array('social' => t('Mostly Public'), 'social_restricted' => t('Restricted'), 'social_private' => t('Private')), + t('Community Forum') => array('forum' => t('Mostly Public'), 'forum_restricted' => t('Restricted'), 'forum_private' => t('Private')), + t('Feed Republish') => array('feed' => t('Mostly Public'), 'feed_restricted' => t('Restricted')), + t('Special Purpose') => array('soapbox' => t('Celebrity/Soapbox'), 'repository' => t('Group Repository')), + t('Other') => array('custom' => t('Custom/Expert Mode'))); - return $o; + return $roles; } diff --git a/library/jquery.AreYouSure/.gitignore b/library/jquery.AreYouSure/.gitignore new file mode 100644 index 000000000..345f0dbeb --- /dev/null +++ b/library/jquery.AreYouSure/.gitignore @@ -0,0 +1,166 @@ +################# +## Eclipse +################# + +*.pydevproject +.project +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + + +################# +## Visual Studio +################# + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +.builds +*.dotCover + +## TODO: If you have NuGet Package Restore enabled, uncomment this +#packages/ + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + + + +############ +## Windows +############ + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + + +############# +## Python +############# + +*.py[co] + +# Packages +*.egg +*.egg-info +dist +build +eggs +parts +bin +var +sdist +develop-eggs +.installed.cfg + +# Installer logs +pip-log.txt + +# Unit test / coverage reports +.coverage +.tox + +#Translations +*.mo + +#Mr Developer +.mr.developer.cfg + +# Mac crap +.DS_Store + +bower_components/ +node_modules/ diff --git a/library/jquery.AreYouSure/Gruntfile.js b/library/jquery.AreYouSure/Gruntfile.js new file mode 100644 index 000000000..efca34c0a --- /dev/null +++ b/library/jquery.AreYouSure/Gruntfile.js @@ -0,0 +1,26 @@ +module.exports = function(grunt) { + grunt.config.init({ + karma: { + options: { + browsers: [ 'Chrome', 'Firefox', 'Safari', 'IE' ], + frameworks: [ 'jasmine' ], + reportSlowerThan: 500, + singleRun: true + }, + unit: { + files: [ + { pattern: 'bower_components/jquery/dist/jquery.min.js' }, + { pattern: 'bower_components/jasmine-jquery/lib/jasmine-jquery.js' }, + { pattern: 'jquery.are-you-sure.js' }, + { pattern: 'spec/javascripts/*.js' }, + { pattern: 'spec/javascripts/fixtures/**/*.html', included: false } + ] + } + } + }); + + grunt.registerTask('test', 'Run tests.', [ 'karma' ]); + grunt.registerTask('default', [ 'test' ]); + + grunt.loadNpmTasks('grunt-karma'); +}; diff --git a/library/jquery.AreYouSure/README.md b/library/jquery.AreYouSure/README.md new file mode 100644 index 000000000..6a538648a --- /dev/null +++ b/library/jquery.AreYouSure/README.md @@ -0,0 +1,297 @@ +Are You Sure? - A light "dirty forms" JQuery Plugin +====== +**Version:** 1.9 + +*Are-you-sure* (```jquery.are-you-sure.js```) is simple light-weight "dirty +form" JQuery Plugin for modern browsers. It helps prevent users from losing +unsaved HTML Form changes by promoting the user to save/submit. + +It's simple to use. Just add the following line to your page's ready +function: + +```javascript +$('form').areYouSure(); +``` + +*Are-you-sure* is a minimal plugin for modern browsers. There are plenty of +"dirty forms" implementations out there, however they all seemed very +heavyweight and over-engineered...! Most were written some time back and +contain many 'hacks' to support legacy browsers, and/or rely on other fat +dependencies such as FaceBox or jQueryUI. *Are-you-sure* solves this by +doing this simple task in the simplest possible way. + +*Are-you-sure* is as simple as it gets: + + * 100% JS with zero dependencies and no external CSS. + * Leverages `onBeforeUnload` to detect all page/browser exit events. + * Works on forms of any size. + * Correct state management - if a user edits then restores a value, the form + is not considered dirty. + * Easy to understand - less than a "terminal screen" of code! + * Graceful degradation on legacy browsers (i.e. if you're running an old + browser... remember to save :-) + +###Basic Usage + +```javascript + +$(function() { + + // Enable on all forms + $('form').areYouSure(); + + // Enable on selected forms + $('form.dirty-check').areYouSure(); + + // With a custom message + $('form').areYouSure( {'message':'Your profile details are not saved!'} ); + +} +``` +To ignore selected fields from the dirtyness check: + +```html + <form id="myForm" name="myform" action="/post" method="post"> + + Field 1: (checked) <input type="text" name="field1"> <br /> + Field 2: (ignored): <input type="text" name="field2" data-ays-ignore="true"> <br /> + Field 3: (ignored): <input type="text" name="field3" class="ays-ignore"> <br /> + + <input type="submit" value="Submit"> + + </form> +``` + +###Advanced Usage + +```javascript + +$(function() { + + /* + * Make Are-You-Sure "silent" by disabling the warning message + * (tracking/monitoring only mode). This option is useful when you wish to + * use the dirty/save events and/or use the dirtyness tracking in your own + * beforeunload handler. + */ + $('form').areYouSure( {'silent':true} ); + + /* + * Dirtyness Change Events + * Are-You-Sure fires off "dirty" and "clean" events when the form's state + * changes. You can bind() or on(), these events to implement your own form + * state logic. A good example is enabling/disabling a Save button. + * + * "this" refers to the form that fired the event. + */ + $('form').on('dirty.areYouSure', function() { + // Enable save button only as the form is dirty. + $(this).find('input[type="submit"]').removeAttr('disabled'); + }); + $('form').on('clean.areYouSure', function() { + // Form is clean so nothing to save - disable the save button. + $(this).find('input[type="submit"]').attr('disabled', 'disabled'); + }); + + /* + * It's easy to test if a form is dirty in your own code - just check + * to see if it has a "dirty" CSS class. + */ + if ($('#my-form').hasClass('dirty')) { + // Do something + } + + /* + * If you're dynamically adding new fields/inputs, and would like to track + * their state, trigger Are-You-Sure to rescan the form like this: + */ + $('#my-form').trigger('rescan.areYouSure'); + + /* + * If you'd like to reset/reinitialize the form's state as clean and + * start tracking again from this new point onwards, trigger the + * reinitalize as follows. This is handy if say you've managing your + * own form save/submit via asyc AJAX. + */ + $('#my-form').trigger('reinitialize.areYouSure'); + + /* + * In some situations it may be desirable to look for other form + * changes such as adding/removing fields. This is useful for forms that + * can change their field count, such as address/phone contact forms. + * Form example, you might remove a phone number from a contact form + * but update nothing else. This should mark the form as dirty. + */ + $('form').areYouSure( {'addRemoveFieldsMarksDirty':true} ); + + /* + * Sometimes you may have advanced forms that change their state via + * custom JavaScript or 3rd-party component JavaScript. Are-You-Sure may + * not automatically detect these state changes. Examples include: + * - Updating a hidden input field via background JS. + * - Using a [rich WYSIWYG edit control](https://github.com/codedance/jquery.AreYouSure/issues/17). + * One solution is to manually trigger a form check as follows: + */ + $('#my-form').trigger('checkform.areYouSure'); + + /* + * As an alternative to using events, you can pass in a custom change + * function. + */ + $('#my-adv-form').areYouSure({ + change: function() { + // Enable save button only if the form is dirty. i.e. something to save. + if ($(this).hasClass('dirty')) { + $(this).find('input[type="submit"]').removeAttr('disabled'); + } else { + $(this).find('input[type="submit"]').attr('disabled', 'disabled'); + } + } + }); + + /* + * Mixing in your own logic into the warning. + */ + $('#my-form').areYouSure( {'silent':true} ); + $(window).on('beforeunload', function() { + isSunday = (0 == (new Date()).getDay()); + if ($('#my-form').hasClass('dirty') && isSunday) { + return "Because it's Sunday, I'll be nice and let you know you forgot to save!"; + } + } + +} +``` +The [demo page](http://www.papercut.com/products/free_software/are-you-sure/demo/are-you-sure-demo.html) +shows the advanced usage options in more detail. + + +###Install +Are-You-Sure is a light-weight jQuery plugin - it's a single standalone +JavaScript file. You can download the +[jquery.are-you-sure.js](https://raw.github.com/codedance/jquery.AreYouSure/master/jquery.are-you-sure.js) +file and include it in your page. Because it's so simple it seems a shame +to add an extra browser round trip. It's recommended that you consider +concatenating it with other common JS lib files, and/or even cut-n-pasting +the code (and license header) into one of your existing JS files. + +For experimental Mobile Safari support, also include ```ays-beforeunload-shim.js``` +(see Known Issues below). + +*Are-you-sure* may also be installed with [Bower](http://twitter.github.com/bower/): + +```bash +$ bower install jquery.are-you-sure +``` + +If you're using, or like, *Are-you-sure* make sure you **star/watch** this project +so you can stay up-to-date with updates. + +###Demo +This [demo page](http://www.papercut.com/products/free_software/are-you-sure/demo/are-you-sure-demo.html) +hosts a number of example forms. + +###Supported Browsers +*Are-you-sure* has been tested on and fully supports the following browsers: + +* IE 9 through 11 +* Google Chrome (versions since 2012) +* Firefox (versions since 2012) +* Safari (versions since 2012) + +Experimental support is available on iOS and Opera via the *beforeunload* shim (see below). + +###Known Issues & Limitations + +####Mobile Safari and Opera +The ```windows.beforeunload``` event is not supported on iOS (iPhone, iPad, and iPod). An +experimental shim offering partial *beforeunload* emulation is provided to help plug this gap. +It works by scanning the page for anchor links and augments the default behaviour to first +check with *Are-you-sure* before navigating away. To use, simply include +```ays-beforeunload-shim.js``` in your page. + +####Firefox +The custom message option may not work on Firefox ([Firefox bug 588292](https://bugzilla.mozilla.org/show_bug.cgi?id=588292)). + +###Development +The aim is to keep *Are-you-sure* simple and light. If you think you have +a good idea which is aligned with this objective, please voice your thoughts +in the issues list. + +####Pull Requests +If possible, please submit your pull request against the most recent ```dev-*``` branch rather than master. This will make it easier to merge your code into the next planned release. + +####Running tests +```bash +$ npm install +$ npm test +``` + +###Release History + +**2014-08-13** (1.9) - This is a minor bugfix release: + +* Addressed issue [#45](https://github.com/codedance/jquery.AreYouSure/issues/55) seen with empty select fields. +* Thanks [valgen](https://github.com/valgen) and [tus124](https://github.com/tus124) for the contribution. + +**2014-06-22** (1.8) - This is a minor bugfix release: + +* Fixed NPE that may occur when using a 'multiple' option field. +* Minor timing tweak to help mitigate bypass issue raised in [#45](https://github.com/codedance/jquery.AreYouSure/issues/45) +* Thanks [apassant](https://github.com/apassant) and [amatenkov](https://github.com/amatenkov) for the contribution. + +**2014-05-28** (1.7) + +* Fixed multiple warning dialogs that may appear on IE and recent versions of Chrome +* Experimental support for iOS Mobile Safari (via a *beforeunload* shim) +* Various minor fixes (e.g. support input fields with no type=) +* Minor performance improvements on pages with multiple forms +* Improved documentation and examples +* Thanks to [lfjeff](https://github.com/lfjeff) and [aqlong](https://github.com/aqlong) for the contribution and ideas! + +**2014-02-07** (1.6) + +* Add field count tracking (```addRemoveFieldsMarksDirty```) (contrib *jonegerton*) +* Added event to manually trigger a form check/recheck (contrib *jonegerton*) +* Thanks to [jonegerton](https://github.com/jonegerton) for the contribution! + +**2013-11-15** (1.5) + +* Added support for HTML5 input field types. (contrib *albinsunnanbo*) +* New option to reinitialize/reset the dirty state. This is handy if you're managing your own async submit/save using AJAX. (contrib *albinsunnanbo*) +* Thanks to [albinsunnanbo](https://github.com/albinsunnanbo) for the contribution! + +**2013-10-2** (1.4) + +* Added dirty and clean "events" +* Added an option to disable the message (dirty tracking only) +* Added an option to rescan a form to look/detect any new fields + +**2013-07-24** - Minor fix - don't fail if form elements have no "name" attribute. + +**2013-05-14** - Added support for form reset buttons (contributed by codev). + +**2013-05-01** - Added support for hidden and disabled form fields. + +**2013-02-03** - Add demo page. + +**2013-01-28** - Add ```change``` event support and a demo page. + +**2012-10-26** - Use dashes in class names rather than camel case. + +**2012-10-24** - Initial public release. + + +###Prerequisites +jQuery version 1.4.2 or higher. 2.0+ or 1.10+ recommended. + + +###License +The same as JQuery... + + jQuery Plugin: Are-You-Sure (Dirty Form Detection) + https://github.com/codedance/jquery.AreYouSure/ + + Copyright (c) 2012-2014, Chris Dance - PaperCut Software http://www.papercut.com/ + Dual licensed under the MIT or GPL Version 2 licenses. + http://jquery.org/license diff --git a/library/jquery.AreYouSure/are-you-sure.jquery.json b/library/jquery.AreYouSure/are-you-sure.jquery.json new file mode 100644 index 000000000..9c699b72b --- /dev/null +++ b/library/jquery.AreYouSure/are-you-sure.jquery.json @@ -0,0 +1,39 @@ +{ + "name": "are-you-sure", + "title": "Are You Sure? - a dirty forms check plugin", + "description": "Are-you-sure is simple light-weight dirty forms JQuery Plugin for modern browsers. It helps prevent users from loosing unsaved form changes by prompting the user to save/submit. It's dependency free and designed for modern browsers... just the features you need and nothing more! See the project page and demo for usage and examples.", + "keywords": [ + "form", + "dirty", + "field", + "change", + "save", + "save-check", + "save-warning" + ], + "version": "1.9.0", + "author": { + "name": "Chris Dance (codedance) at PaperCut Software", + "url": "https://github.com/codedance" + }, + "maintainers": [ + { + "name": "Chris Dance", + "email": "chris.dance@papercut.com", + "url": "http://www.papercut.com/" + } + ], + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/codedance/jquery.AreYouSure/blob/master/README.md" + } + ], + "bugs": "https://github.com/codedance/jquery.AreYouSure/issues", + "homepage": "https://github.com/codedance/jquery.AreYouSure", + "docs": "https://github.com/codedance/jquery.AreYouSure", + "demo": "http://www.papercut.com/products/free_software/are-you-sure/demo/are-you-sure-demo.html", + "dependencies": { + "jquery": ">=1.4.2" + } +} diff --git a/library/jquery.AreYouSure/ays-beforeunload-shim.js b/library/jquery.AreYouSure/ays-beforeunload-shim.js new file mode 100644 index 000000000..cb864cdeb --- /dev/null +++ b/library/jquery.AreYouSure/ays-beforeunload-shim.js @@ -0,0 +1,31 @@ +/*! + * An experimental shim to partially emulate onBeforeUnload on iOS. + * Part of https://github.com/codedance/jquery.AreYouSure/ + * + * Copyright (c) 2012-2014, Chris Dance and PaperCut Software http://www.papercut.com/ + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Author: chris.dance@papercut.com + * Date: 19th May 2014 + */ +$(function() { + if (!navigator.userAgent.toLowerCase().match(/iphone|ipad|ipod|opera/)) { + return; + } + $('a').bind('click', function(evt) { + var href = $(evt.target).closest('a').attr('href'); + if (href !== undefined && !(href.match(/^#/) || href.trim() == '')) { + var response = $(window).triggerHandler('beforeunload', response); + if (response && response != "") { + var msg = response + "\n\n" + + "Press OK to leave this page or Cancel to stay."; + if (!confirm(msg)) { + return false; + } + } + window.location.href = href; + return false; + } + }); +}); diff --git a/library/jquery.AreYouSure/bower.json b/library/jquery.AreYouSure/bower.json new file mode 100644 index 000000000..8591dc0d0 --- /dev/null +++ b/library/jquery.AreYouSure/bower.json @@ -0,0 +1,30 @@ +{ + "name": "jquery.are-you-sure", + "version": "1.9.0", + "homepage": "https://github.com/codedance/jquery.AreYouSure", + "authors": [ + "CodeDance <chris.dance@papercut.com>" + ], + "description": "A light-weight jQuery 'dirty forms' Plugin - it monitors html forms and alerts users to unsaved changes if they attempt to close the browser or navigate away from the page. (Are you sure?)", + "main": "jquery.are-you-sure.js", + "keywords": [ + "form", + "dirty", + "field", + "change", + "save-check", + "are-you-sure", + "save-warning" + ], + "license": "MIT/GPLv2", + "ignore": [ + "**/.*", + "demo" + ], + "dependencies": { + "jquery": ">=1.4.2" + }, + "devDependencies": { + "jasmine-jquery": "~2.0.3" + } +} diff --git a/library/jquery.AreYouSure/demo/are-you-sure-demo.html b/library/jquery.AreYouSure/demo/are-you-sure-demo.html new file mode 100644 index 000000000..3f0327b2e --- /dev/null +++ b/library/jquery.AreYouSure/demo/are-you-sure-demo.html @@ -0,0 +1,576 @@ +<!DOCTYPE html> +<html> + <head> + <title>Demo: Are You Sure? - a dirty forms jQuery Plugin</title> + + <!-- + + We'll use an old version of jQuery to show we're backwards compatible. In + production we recommend using a later version. e.g. + + <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script> + <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script> + + --> + + <script src="http://code.jquery.com/jquery-1.4.2.min.js"></script> + <script src="../jquery.are-you-sure.js"></script> + <script src="../ays-beforeunload-shim.js"></script> + <script> + + $(function() { + + // Example 1 - ... in one line of code + $('#example-1-form').areYouSure(); + + + // Example 2 - ignore a dynamic field + $('#example-2-form').areYouSure(); + + var defaultPickup15min = new Date((new Date()).getTime() + 15 * 60000); + $('#pickup').val(defaultPickup15min.getHours() + + ':' + defaultPickup15min.getMinutes()); + + + // Example 3 - custom message and hooking the dirty change events + $('#example-3-form').areYouSure( + { + message: "Did you forget to save your standard coffee order?" + } + ); + // Enable save button only if the form is dirty - using events. + $('#example-3-form').bind('dirty.areYouSure', function() { + $(this).find('input[type="submit"]').removeAttr('disabled'); + }); + $('#example-3-form').bind('clean.areYouSure', function() { + $(this).find('input[type="submit"]').attr('disabled', 'disabled'); + }); + + + // Example 4 - dynamically change and add form fields. + $('#example-4-form').areYouSure( + { + message: "Did you forget to submit your coffee order?" + } + ); + + $('#example-4-lastorder').click(function() { + // ... set our saved coffee type. + $('#example-4-coffee').val('espresso'); + // Because we've made a change from our own JavaScript, we need to fire + // off manual 'form check'. + $('#example-4-form').trigger('checkform.areYouSure'); + }); + + // If it's warm enough, offer an iced coffee special. + $.getJSON("http://api.openweathermap.org/data/2.5/weather?q=Melbourne,AU&mode=json&units=metric&callback=?", + function(data) { + var temp = data.main.temp; + if (temp > 5) { + $('#example-4-special').append('<p>It\'s currently ' + + temp + 'C in Melbourne. Ice it up!</p>'); + $('#example-4-special').append('<input type="checkbox" name="make-it-iced" value="true" />' + + ' Make it an iced coffee<br />'); + + // Trigger rescan event on the form so we start tracking the new field. + $('#example-4-form').trigger('rescan.areYouSure'); + } + } + ); + + + /* + * Example 5 + * - extra shots button to enable/disable shots options + * - like/unlike button (changing hidden form field value) + * - hook dirty change event to enable/disable submit (using method), + * to more easily demonstrate when the form is dirty + */ + $('#example-5-extra-shots').click(function() { + // we trigger a change event on the fields so that the AreYouSure event handler is called + $('#example-5-form input[name=shots]').removeAttr('disabled').change(); + $('#example-5-extra-shots').hide(); + $('#example-5-shots-options').show(); + return false; + }); + $('#example-5-like-button').click(function() { + var currentLike = $('#example-5-like').val() == 'true'; + var newLike = !currentLike; + // we trigger a change event on the fields so that the areYouSure event handler is called + $('#example-5-like').val(newLike).change(); + $('#example-5-like-button').text(newLike ? 'Unlike' : 'Like'); + return false; + }); + $('#example-5-form').areYouSure({ + change: function() { + // Enable save button only if the form is dirty. + if ($(this).hasClass('dirty')) { + $(this).find('input[type="submit"]').removeAttr('disabled'); + } else { + $(this).find('input[type="submit"]').attr('disabled', 'disabled'); + } + } + }); + + // Example 6 - HTML5 input types + $('#example-6-form').areYouSure(); + + // Example 7 - ... in one line of code for the form and some more optional to toggle disabled state of the save button + $('#example-7-form').areYouSure(); + $('#example-7-save-button').click(function () { + $('#example-7-form').trigger('reinitialize.areYouSure'); + }); + // code below is optional to handle disabled state of the save button + $('#example-7-form').bind('dirty.areYouSure', function () { + // Enable save button only as the form is dirty. + $('#example-7-save-button').attr({ 'disabled': false }); + }); + $('#example-7-form').bind('clean.areYouSure', function () { + // Form is clean so nothing to save - disable the save button. + $('#example-7-save-button').attr({ 'disabled': true }); + }); + }); + + </script> + + <style type="text/css"> + body { + font-family: myriad-pro-1, myriad-pro-2, 'Lucida Grande', 'Arial', sans-serif; + margin: 25px; + } + + form { + width: 350px; + border: 1px solid #AA5303; + padding: 10px 20px; + background-image: -webkit-linear-gradient(top, rgba(170,83,3, 0.1), rgba(170,83,3, 0.5)); + background-image: -moz-linear-gradient(top, rgba(170,83,3, 0.1), rgba(170,83,3, 0.5)); + background-image: -ms-linear-gradient(top, rgba(170,83,3, 0.1), rgba(170,83,3, 0.5)); + background-image: -o-linear-gradient(top, rgba(170,83,3, 0.1), rgba(170,83,3, 0.5)); + background-image: linear-gradient(top, rgba(170,83,3, 0.1), rgba(170,83,3, 0.5)); + border-radius: 5px; + } + + /* A bit of custom styling on example 3 */ + #example-3-form.dirty, #example-4-form.dirty { + box-shadow: 0 0 8px rgba(255, 0, 0, 1); + -webkit-box-shadow: 0 0 8px rgba(255, 0, 0, 1); + -moz-box-shadow: 0 0 8px rgba(255, 0, 0, 1); + border:1px solid rgba(255,0,0, 0.8); + } + + form h2 { + font-size: 22px; + } + + form > div { + padding: 8px; + font-size: 12px; + } + form > div input[type="text"], + form > div input[type="color"], + form > div input[type="date"], + form > div input[type="datetime"], + form > div input[type="datetime-local"], + form > div input[type="email"], + form > div input[type="month"], + form > div input[type="number"], + form > div input[type="range"], + form > div input[type="search"], + form > div input[type="tel"], + form > div input[type="time"], + form > div input[type="url"], + form > div input[type="week"], + form > div input:not([type]), + form > div textarea, + form > div select, + form > div button { + float: right; + width: 200px; + } + + form > div input[type="radio"], + form > div input[type="checkbox"] { + display: inline-block; + margin-left: 130px; + } + + form > div input[type="submit"] { + float: right; + } + form > div.buttons { + clear: both; + padding-bottom: 20px; + } + </style> + </head> + + <body> + <h1>jQuery Plugin Demo: Are You Sure?</h1> + <p> + This page hosts a demo of the <a href="https://github.com/codedance/jquery.AreYouSure">jQuery Are-You-Sure</a> plugin (<code>jquery.are-you-sure.js</code>). + </p> + <p> + <i>Are-you-sure</i> is simple light-weight "dirty forms" JQuery Plugin for modern browsers. It helps prevent users from loosing unsaved form changes. + </p> + <p> + <strong>Features:</strong> + <ul> + <li>Light weight - only the features you need!</li> + <li>Dependency free.</li> + <li>Correct state management - if a user edits then restores a value, the form is not considered dirty.</li> + <li>Easy to understand - less than a "terminal screen" of code!</li> + <li>... and <a href="https://github.com/codedance/jquery.AreYouSure">more</a>.</li> + </ul> + </p> + + <h2>Example 1: It's simple!</h2> + <p> + This example shows how easy it is to add a dirty check to your form(s) with one line + of code. (View the page's source) + </p> + + <form id="example-1-form" name="coffeeOrder1" method="post"> + <h2>Enter Your Coffee Order</h2> + <div> + <label for="example-1-coffee">Coffee</label> + <select name="coffee" id="example-1-coffee"> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + <option value="macciato">Machhiato</option> + <option value="cappuccino">Cappuccino</option> + </select> + </div> + <div> + <label>Shots</label><br /> + <input type="radio" name="shots" value="1" checked /> 1 Shot - Standard<br /> + <input type="radio" name="shots" value="2" /> 2 Shots - Morning wakeup!<br /> + <input type="radio" name="shots" value="3" /> 3 Shots - Overdrive!<br /> + </div> + <div> + <label for="example-1-sugar">Sugar</label> <input type="text" name="sugar" value="0" id="example-1-sugar" /> + </div> + <div> + <label for="example-1-instructions">Special Instructions</label> + <textarea name="instructions" rows="5" id="example-1-instructions"></textarea> + </div> + + <div class="buttons"> + <input type="checkbox" name="remember" value="true" /> Remember my order<br /> + <input type="submit" value="Submit"> + <input type="reset" value="Reset"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + <h2>Example 2: Ignore the unimportant!</h2> + <p> + This example highlights how to disregard a field from the dirty check. In this form + the first field is dynamically populated and hence a change on this field should <em>not</em> + mark the form as dirty. + </p> + <form id="example-2-form" name="coffeeOrder2" method="post"> + <h2>Enter Your Coffee Order</h2> + <div> + <!-- The ays-ignore class means a change on this field is not considered "dirty" --> + <label for="pickup">Pickup Time</label> + <input class="ays-ignore" type="text" name="pickup" id="pickup" /> + <!-- or you can use a data attribute like this: + <label for="pickup">Pickup Time</label> <input data-ays-ignore="true" type="text" name="pickup" id="pickup" /> + --> + </div> + <div> + <label for="example-2-coffee">Coffee</label> + <select name="coffee" id="example-2-coffee"> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + <option value="macciato">Machhiato</option> + <option value="cappuccino">Cappuccino</option> + </select> + </div> + <div> + <label>Shots</label><br /> + <input type="radio" name="shots" value="1" checked /> 1 Shot - Standard<br /> + <input type="radio" name="shots" value="2" /> 2 Shots - Morning wakeup!<br /> + <input type="radio" name="shots" value="3" /> 3 Shots - Overdrive!<br /> + </div> + <div> + <label for="example-2-sugar">Sugar</label> <input type="text" name="sugar" value="0" id="example-2-sugar" /> + </div> + <div> + <label for="example-2-instructions">Special Instructions</label> + <textarea name="instructions" rows="5" id="example-2-instructions"></textarea> + </div> + + <div class="buttons"> + <input type="checkbox" name="remember" value="true" /> Remember my order<br /> + <input type="submit" value="Submit"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + + <h2>Example 3: Lets be intelligent!</h2> + <p> + This is a more advanced example. The <code>dirty</code> and <code>clean</code> change events are + intercepted so the save button is only enabled if the form is dirty (i.e. something to save). + It also demonstrates how to customize the warning message and change the style of a dirty + form (CSS styling using the <code>.dirty</code> class). + </p> + <form id="example-3-form" name="coffeeOrder3" method="post"> + <h2>Update My Standard Order</h2> + <div> + <label for="example-3-coffee">Coffee</label> + <select name="coffee" id="example-3-coffee"> + <option value=""></option> + <option value="cappuccino">Cappuccino</option> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + <option value="macciato">Machhiato</option> + </select> + </div> + <div> + <label>Shots</label><br /> + <input type="radio" name="shots" value="1" /> 1 Shot - Standard<br /> + <input type="radio" name="shots" value="2" checked /> 2 Shots - Morning wakeup!<br /> + <input type="radio" name="shots" value="3" /> 3 Shots - Overdrive!<br /> + </div> + <div> + <label for="example-3-sugar">Sugar</label> + <input type="text" name="sugar" value="1" id="example-3-sugar" /> + </div> + <div> + <label for="example-3-instructions">Special Instructions</label> + <textarea name="instructions" rows="5" id="example-3-instructions">No chocolate please</textarea> + </div> + + <div class="buttons"> + <input type="submit" value="Save" disabled="disabled"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + <h2>Example 4: Lets be dynamic!</h2> + <p> + In this example we'll dymaically add a field and fire off the <code>rescan</code> event. After + the rescan, Are-You-Sure will start looking for changes on the new fields as well. + </p> + <form id="example-4-form" name="coffeeOrder4" method="post"> + <h2>Order Coffee For Pickup Now</h2> + <div> + <a id="example-4-lastorder" href="javascript:void(0);">Set my preferences from my last order</a><br /> + </div> + <div> + <label for="example-4-coffee">Coffee</label> + <select name="coffee" id="example-4-coffee"> + <option value="cappuccino">Cappuccino</option> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + </select> + </div> + <div id="example-4-special"></div> + <div class="buttons"> + <input type="submit" value="Place Order"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + <h2>Example 5: Edge cases</h2> + <p> + This example demonstrates tracking of hidden and disabled form elements that are changed by non-input elements. + E.g.: + </p> + <ul> + <li>clicking a link or non-input button that changes the value of a hidden form field, or</li> + <li> + clicking a link or non-input button that enables or disables some form fields (which has an effect on whether + or not those fields will be submitted with the form, despite the values not changing). + </li> + </ul> + <form id="example-5-form" name="coffeeOrder5" method="post"> + <h2>Update My Standard Order</h2> + <div> + <label for="example-5-coffee">Coffee</label> + <select name="coffee" id="example-3-coffee"> + <option value="cappuccino">Cappuccino</option> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + <option value="macciato">Machhiato</option> + </select> + </div> + <div> + <label>Shots</label> + <!-- + The visually hidden "shots" radio buttons are disabled and would not normally be submitted with the form. + Clicking the following button will visually show and enable these buttons, resulting in a form state change + (and hence the form is considered dirty) without the values having changed. + --> + <button id="example-5-extra-shots">I want extra shots</button> + <div id="example-5-shots-options" style="display: none;"> + <input type="radio" name="shots" value="1" disabled /> 1 Shot - Standard<br /> + <input type="radio" name="shots" value="2" disabled /> 2 Shots - Morning wakeup!<br /> + <input type="radio" name="shots" value="3" disabled /> 3 Shots - Overdrive!<br /> + </div> + </div> + <div> + <label>Do you like us?</label> + <input type="hidden" name="like" value="false" id="example-5-like" /> + <button id="example-5-like-button">Like</button> + </div> + <div class="buttons"> + <input type="submit" value="Save" disabled="disabled"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + <h2>Example 6: HTML5 inputs!</h2> + <p> + This example shows support for HTML5 input types. It's not a coffee order form, + but you need coffee if you're working with HTML5 :-) + </p> + + <form id="example-6-form" name="coffeeOrder6" method="post"> + <h2>Doing HTML5? You'll need coffee!</h2> + <div> + <label for="example-6-color">color</label> + <input type="color" name="example-6-color" /> + </div> + <div> + <label for="example-6-date">date</label> + <input type="date" name="example-6-date" /> + </div> + <div> + <label for="example-6-datetime">datetime</label> + <input type="datetime" name="example-6-datetime" /> + </div> + <div> + <label for="example-6-datetime-local">datetime-local</label> + <input type="datetime-local" name="example-6-datetime-local" /> + </div> + <div> + <label for="example-6-email">email</label> + <input type="email" name="example-6-email" /> + </div> + <div> + <label for="example-6-month">month</label> + <input type="month" name="example-6-month" /> + </div> + <div> + <label for="example-6-number">number</label> + <input type="number" name="example-6-number" /> + </div> + <div> + <label for="example-6-range">range</label> + <input type="range" name="example-6-range" /> + </div> + <div> + <label for="example-6-search">search</label> + <input type="search" name="example-6-search" /> + </div> + <div> + <label for="example-6-tel">tel</label> + <input type="tel" name="example-6-tel" /> + </div> + <div> + <label for="example-6-time">time</label> + <input type="time" name="example-6-time" /> + </div> + <div> + <label for="example-6-url">url</label> + <input type="url" name="example-6-url" /> + </div> + <div> + <label for="example-6-week">week</label> + <input type="week" name="example-6-week" /> + </div> + <div> + <label for="example-6-select">select/optgroup</label> + <select> + <optgroup label="Beans"> + <option value="india">India</option> + <option value="indonesia" selected="selected">Indonesia</option> + <option value="brazil">Brazil</option> + </optgroup> + <optgroup label="Roast"> + <option value="l">Light</option> + <option value="m">Medium</option> + <option value="h">Heavy</option> + </optgroup> + </select> + </div> + <div> + <label for="example-6-mselect">multi select</label> + <select multiple> + <option value="arabica">Arabica</option> + <option value="arabica">Peaberry</option> + <option value="malabar " selected="selected">Malabar</option> + <option value="robusta ">Robusta</option> + </select> + </div> + <div style="clear:both;"></div> + <div> + <label for="example-6-plain">plain old field</label> + <input name="example-6-plain" /> + </div> + + <div class="buttons"> + <input type="submit" value="Submit"> + <input type="reset" value="Reset"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + <h2>Example 7: Mark current state as not dirty!</h2> + <p> + This example shows how you can mark the current state as not dirty. Handy for AJAX forms + we're you're managing your own submits. + </p> + + <form id="example-7-form" name="coffeeOrder7" method="post"> + <h2>Tell us your default coffee</h2> + <div> + <label for="example-7-coffee">Default coffee</label> + <select name="coffee" id="example-7-coffee"> + <option value="espresso">Espresso</option> + <option value="dbl-espresso">Caffe Doppio</option> + <option value="latte">Caffe Latte</option> + <option value="macciato">Machhiato</option> + <option value="cappuccino">Cappuccino</option> + </select> + </div> + <div> + <button id="example-7-save-button" disabled="disabled">Save without submit</button> + </div> + <div class="buttons"> + <input type="submit" value="Submit"> + </div> + <div> + <p>... or visit <a href="http://www.google.com/">Google</a> or close the window without saving!</p> + </div> + </form> + + <p> + This jQuery plugin is developed by <a href="https://github.com/codedance">Chris Dance</a> + at <a href="http://www.papercut.com/">PaperCut Software</a> - Are-You-Sure is used in + PaperCut's printing management software and it has been open sourced with help of + Tom, Jack and Matt from PaperCut's dev team. + </p> + + </body> +</html> diff --git a/library/jquery.AreYouSure/jquery.are-you-sure.js b/library/jquery.AreYouSure/jquery.are-you-sure.js new file mode 100644 index 000000000..3c41e2fcc --- /dev/null +++ b/library/jquery.AreYouSure/jquery.are-you-sure.js @@ -0,0 +1,192 @@ +/*! + * jQuery Plugin: Are-You-Sure (Dirty Form Detection) + * https://github.com/codedance/jquery.AreYouSure/ + * + * Copyright (c) 2012-2014, Chris Dance and PaperCut Software http://www.papercut.com/ + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Author: chris.dance@papercut.com + * Version: 1.9.0 + * Date: 13th August 2014 + */ +(function($) { + + $.fn.areYouSure = function(options) { + + var settings = $.extend( + { + 'message' : 'You have unsaved changes!', + 'dirtyClass' : 'dirty', + 'change' : null, + 'silent' : false, + 'addRemoveFieldsMarksDirty' : false, + 'fieldEvents' : 'change keyup propertychange input', + 'fieldSelector': ":input:not(input[type=submit]):not(input[type=button])" + }, options); + + var getValue = function($field) { + if ($field.hasClass('ays-ignore') + || $field.hasClass('aysIgnore') + || $field.attr('data-ays-ignore') + || $field.attr('name') === undefined) { + return null; + } + + if ($field.is(':disabled')) { + return 'ays-disabled'; + } + + var val; + var type = $field.attr('type'); + if ($field.is('select')) { + type = 'select'; + } + + switch (type) { + case 'checkbox': + case 'radio': + val = $field.is(':checked'); + break; + case 'select': + val = ''; + $field.find('option').each(function(o) { + var $option = $(this); + if ($option.is(':selected')) { + val += $option.val(); + } + }); + break; + default: + val = $field.val(); + } + + return val; + }; + + var storeOrigValue = function($field) { + $field.data('ays-orig', getValue($field)); + }; + + var checkForm = function(evt) { + + var isFieldDirty = function($field) { + var origValue = $field.data('ays-orig'); + if (undefined === origValue) { + return false; + } + return (getValue($field) != origValue); + }; + + var $form = ($(this).is('form')) + ? $(this) + : $(this).parents('form'); + + // Test on the target first as it's the most likely to be dirty + if (isFieldDirty($(evt.target))) { + setDirtyStatus($form, true); + return; + } + + $fields = $form.find(settings.fieldSelector); + + if (settings.addRemoveFieldsMarksDirty) { + // Check if field count has changed + var origCount = $form.data("ays-orig-field-count"); + if (origCount != $fields.length) { + setDirtyStatus($form, true); + return; + } + } + + // Brute force - check each field + var isDirty = false; + $fields.each(function() { + $field = $(this); + if (isFieldDirty($field)) { + isDirty = true; + return false; // break + } + }); + + setDirtyStatus($form, isDirty); + }; + + var initForm = function($form) { + var fields = $form.find(settings.fieldSelector); + $(fields).each(function() { storeOrigValue($(this)); }); + $(fields).unbind(settings.fieldEvents, checkForm); + $(fields).bind(settings.fieldEvents, checkForm); + $form.data("ays-orig-field-count", $(fields).length); + setDirtyStatus($form, false); + }; + + var setDirtyStatus = function($form, isDirty) { + var changed = isDirty != $form.hasClass(settings.dirtyClass); + $form.toggleClass(settings.dirtyClass, isDirty); + + // Fire change event if required + if (changed) { + if (settings.change) settings.change.call($form, $form); + + if (isDirty) $form.trigger('dirty.areYouSure', [$form]); + if (!isDirty) $form.trigger('clean.areYouSure', [$form]); + $form.trigger('change.areYouSure', [$form]); + } + }; + + var rescan = function() { + var $form = $(this); + var fields = $form.find(settings.fieldSelector); + $(fields).each(function() { + var $field = $(this); + if (!$field.data('ays-orig')) { + storeOrigValue($field); + $field.bind(settings.fieldEvents, checkForm); + } + }); + // Check for changes while we're here + $form.trigger('checkform.areYouSure'); + }; + + var reinitialize = function() { + initForm($(this)); + } + + if (!settings.silent && !window.aysUnloadSet) { + window.aysUnloadSet = true; + $(window).bind('beforeunload', function() { + $dirtyForms = $("form").filter('.' + settings.dirtyClass); + if ($dirtyForms.length == 0) { + return; + } + // Prevent multiple prompts - seen on Chrome and IE + if (navigator.userAgent.toLowerCase().match(/msie|chrome/)) { + if (window.aysHasPrompted) { + return; + } + window.aysHasPrompted = true; + window.setTimeout(function() {window.aysHasPrompted = false;}, 900); + } + return settings.message; + }); + } + + return this.each(function(elem) { + if (!$(this).is('form')) { + return; + } + var $form = $(this); + + $form.submit(function() { + $form.removeClass(settings.dirtyClass); + }); + $form.bind('reset', function() { setDirtyStatus($form, false); }); + // Add a custom events + $form.bind('rescan.areYouSure', rescan); + $form.bind('reinitialize.areYouSure', reinitialize); + $form.bind('checkform.areYouSure', checkForm); + initForm($form); + }); + }; +})(jQuery); diff --git a/library/jquery.AreYouSure/package.json b/library/jquery.AreYouSure/package.json new file mode 100644 index 000000000..0b4c38dde --- /dev/null +++ b/library/jquery.AreYouSure/package.json @@ -0,0 +1,45 @@ +{ + "name": "jquery.AreYouSure", + "description": "A light-weight jQuery \"dirty forms\" Plugin - it monitors HTML forms and alerts users to unsaved changes if they attempt to close the browser or navigate away from the page. (Are you sure?)", + "homepage": "https://github.com/codedance/jquery.AreYouSure", + "author": "Chris Dance <chris.dance@papercut.com> (https://github.com/codedance)", + "contributors": [ + "Tom Clift <tom.clift@papercut.com> (https://github.com/tclift)", + "Jon Egerton <jon@ja2.co.uk> (http://www.jonegerton.com/)", + "Scadoodles (https://github.com/Scadoodles)", + "Albin Sunnanbo (https://github.com/albinsunnanbo)", + "Marc Sutton <ashre@iname.com> (http://www.codev.co.uk)" + ], + "version": "1.9.0", + "license": "MIT/GPLv2", + "keywords": [ "dirty", "form", "onbeforeunload", "save", "check" ], + "main": "jquery.are-you-sure.js", + "engines": { + "node": ">=0.8.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/codedance/jquery.AreYouSure" + }, + "bugs": { + "url": "https://github.com/codedance/jquery.AreYouSure/issues" + }, + "dependencies": { + "jquery": ">=1.4.2" + }, + "devDependencies": { + "bower": "^1.3.1", + "grunt": "^0.4.5", + "grunt-cli": "^0.1.13", + "grunt-karma": "^0.8.3", + "karma-chrome-launcher": "^0.1.4", + "karma-jasmine": "^0.2.2", + "karma-ie-launcher": "^0.1.5", + "karma-firefox-launcher": "^0.1.3", + "karma-safari-launcher": "^0.1.1" + }, + "scripts": { + "postinstall": "node_modules/.bin/bower install", + "test": "node_modules/.bin/grunt test" + } +} diff --git a/library/jquery.AreYouSure/spec/javascripts/fixtures/input-text.html b/library/jquery.AreYouSure/spec/javascripts/fixtures/input-text.html new file mode 100644 index 000000000..1b2850759 --- /dev/null +++ b/library/jquery.AreYouSure/spec/javascripts/fixtures/input-text.html @@ -0,0 +1,4 @@ +<form> + <input type="text" name="a"> + <input type="submit"> +</form> diff --git a/library/jquery.AreYouSure/spec/javascripts/jquery.are-you-sure_spec.js b/library/jquery.AreYouSure/spec/javascripts/jquery.are-you-sure_spec.js new file mode 100644 index 000000000..5e02f7cb0 --- /dev/null +++ b/library/jquery.AreYouSure/spec/javascripts/jquery.are-you-sure_spec.js @@ -0,0 +1,28 @@ +'use strict'; + +// Karma adds 'base/' to the default path +jasmine.getFixtures().fixturesPath = 'base/spec/javascripts/fixtures'; + +describe("A form's", function() { + var $form = undefined; + + describe('text input', function() { + var $textInput = undefined; + + beforeEach(function() { + loadFixtures('input-text.html'); + $form = $('form'); + $textInput = $('input[type=text]'); + $form.areYouSure(); + }); + + it('should cause dirtyness after its value changes', function(done) { + expect($form.hasClass('dirty')).toBe(false); + $textInput.val('new').change(); + setTimeout(function() { + expect($form.hasClass('dirty')).toBe(true); + done(); + }, 0); + }); + }); +}); diff --git a/mod/new_channel.php b/mod/new_channel.php index 185fc7c28..047048f0a 100644 --- a/mod/new_channel.php +++ b/mod/new_channel.php @@ -115,11 +115,8 @@ function new_channel_content(&$a) { '$nick_desc' => t('Your nickname will be used to create an easily remembered channel address (like an email address) which you can share with others.'), '$label_import' => t('Or <a href="import">import an existing channel</a> from another location'), '$name' => $name, - '$label_role' => t('Channel Type'), - '$questionmark' => t('?'), - '$what_is_role' => t('What is this?'), - '$help_role' => t('Please choose a channel type (such as social networking or community forum) and privacy requirements so we can select the best permissions for you'), - '$role_select' => role_selector(($privacy_role) ? $privacy_role : 'social'), + '$help_role' => t('Please choose a channel type (such as social networking or community forum) and privacy requirements so we can select the best permissions for you'), + '$role' => array('permissions_role' , t('Channel Type'), ($privacy_role) ? $privacy_role : 'social', '<a href="help/roles" target="_blank">'.t('Read more about roles').'</a>',get_roles()), '$nickname' => $nickname, '$submit' => t('Create') )); diff --git a/mod/profiles.php b/mod/profiles.php index f2695f332..6bdc7f11a 100644 --- a/mod/profiles.php +++ b/mod/profiles.php @@ -596,13 +596,12 @@ function profiles_content(&$a) { $opt_tpl = get_markup_template("profile_hide_friends.tpl"); - $hide_friends = replace_macros($opt_tpl,array( - '$desc' => t('Hide your contact/friend list from viewers of this profile?'), - '$yes_str' => t('Yes'), - '$no_str' => t('No'), - '$yes_selected' => (($r[0]['hide_friends']) ? " checked=\"checked\" " : ""), - '$no_selected' => (($r[0]['hide_friends'] == 0) ? " checked=\"checked\" " : "") - )); + $hide_friends = replace_macros($opt_tpl,array('$field' => array( + 'hide-friends', + t('Hide your contact/friend list from viewers of this profile?'), + $r[0]['hide_friends'], + '', + ))); $q = q("select * from profdef where true"); if($q) { diff --git a/mod/settings.php b/mod/settings.php index be6f2cfb9..59ce98a2a 100644 --- a/mod/settings.php +++ b/mod/settings.php @@ -122,10 +122,19 @@ function settings_post(&$a) { if((argc() > 1) && (argv(1) === 'features')) { check_form_security_token_redirectOnErr('/settings/features', 'settings_features'); - foreach($_POST as $k => $v) { - if(strpos($k,'feature_') === 0) { - set_pconfig(local_user(),'feature',substr($k,8),((intval($v)) ? 1 : 0)); - } + + // Build list of features and check which are set + $features = get_features(); + $all_features = array(); + foreach($features as $k => $v) { + foreach($v as $f) + $all_features[] = $f[0]; + } + foreach($all_features as $k) { + if(x($_POST,"feature_$k")) + set_pconfig(local_user(),'feature',$k, 1); + else + set_pconfig(local_user(),'feature',$k, 0); } build_sync_packet(); return; @@ -707,7 +716,6 @@ function settings_content(&$a) { '$title' => t('Additional Features'), '$features' => $arr, '$submit' => t('Submit'), - '$field_yesno' => 'field_yesno.tpl', )); return $o; @@ -938,7 +946,7 @@ function settings_content(&$a) { $timezone = date_default_timezone_get(); - $opt_tpl = get_markup_template("field_yesno.tpl"); + $opt_tpl = get_markup_template("field_checkbox.tpl"); if(get_config('system','publish_all')) { $profile_in_dir = '<input type="hidden" name="profile_in_directory" value="1" />'; } @@ -1010,12 +1018,10 @@ function settings_content(&$a) { '$uid' => local_user(), '$form_security_token' => get_form_security_token("settings"), '$nickname_block' => $prof_addr, - - '$h_basic' => t('Basic Settings'), '$username' => array('username', t('Full Name:'), $username,''), '$email' => array('email', t('Email Address:'), $email, ''), - '$timezone' => array('timezone_select' , t('Your Timezone:'), select_timezone($timezone), ''), + '$timezone' => array('timezone_select' , t('Your Timezone:'), $timezone, '', get_timezones()), '$defloc' => array('defloc', t('Default Post Location:'), $defloc, t('Geographical location to display on your posts')), '$allowloc' => array('allow_location', t('Use Browser Location:'), ((get_pconfig(local_user(),'system','use_browser_location')) ? 1 : ''), ''), @@ -1044,9 +1050,7 @@ function settings_content(&$a) { '$aclselect' => populate_acl($perm_defaults,false), '$suggestme' => $suggestme, '$group_select' => $group_select, - '$role_lbl' => t('Channel permissions category:'), - - '$role_select' => role_selector($permissions_role), + '$role' => array('permissions_role' , t('Channel permissions category:'), $permissions_role, '', get_roles()), '$profile_in_dir' => $profile_in_dir, '$hide_friends' => $hide_friends, diff --git a/mod/setup.php b/mod/setup.php index 044def15a..6f2c7c074 100755 --- a/mod/setup.php +++ b/mod/setup.php @@ -349,7 +349,7 @@ function setup_content(&$a) { '$siteurl' => array('siteurl', t('Website URL'), z_root(), t('Please use SSL (https) URL if available.')), - '$timezone' => field_timezone('timezone', t('Please select a default timezone for your website'), $timezone, ''), + '$timezone' => array('timezone', t('Please select a default timezone for your website'), $timezone, '', get_timezones()), '$baseurl' => $a->get_baseurl(), diff --git a/view/css/bootstrap-red.css b/view/css/bootstrap-red.css index 12287ba4f..5d9bb6e90 100644 --- a/view/css/bootstrap-red.css +++ b/view/css/bootstrap-red.css @@ -79,3 +79,7 @@ nav .navbar-toggle { code { white-space: normal; } + +/* Bootstrap assumes that checkboxes are on the left of labels, while it's usually the opposite in Red */ +.field.checkbox input[type="checkbox"] { margin-left: 0px; } +.field.checkbox label { padding-left: 0px; font-weight: 700} diff --git a/view/css/mod_connect.css b/view/css/mod_connect.css index d1a46ec48..218b1d2cb 100644 --- a/view/css/mod_connect.css +++ b/view/css/mod_connect.css @@ -6,6 +6,8 @@ margin-top: 25px; } -#sellpage-edit label{ +/* first-of-type needed to style switches */ +#sellpage-edit label.mainlabel, +#sellpage-edit label:first-of-type { width: 300px; -}
\ No newline at end of file +} diff --git a/view/css/mod_group.css b/view/css/mod_group.css index cc5f15843..30a954d2a 100644 --- a/view/css/mod_group.css +++ b/view/css/mod_group.css @@ -3,7 +3,9 @@ margin-top: 30px; } -#group-edit-form label { +/* first-of-type needed to style switches */ +#group-edit-form label.mainlabel, +#group-edit-form label:first-of-type { float: left; width: 300px; } diff --git a/view/css/mod_settings.css b/view/css/mod_settings.css index b03023e21..b066e6059 100644 --- a/view/css/mod_settings.css +++ b/view/css/mod_settings.css @@ -28,7 +28,9 @@ ul#settings-privacy-macros { margin-bottom: 10px; } -#settings-permissions-wrapper .field label{ +/* first-of-type needed to be able to style switches */ +#settings-permissions-wrapper .field label.mainlabel, +#settings-permissions-wrapper .field label:first-of-type { width: 350px; } @@ -41,7 +43,9 @@ ul#settings-privacy-macros { margin-bottom: 45px; } -#settings-notifications label { +/* first-of-type needed to be able to style switches */ +#settings-notifications .field label.mainlabel, +#settings-notifications .field label:first-of-type { margin-left: 20px; width: 330px; } @@ -63,4 +67,4 @@ ul#settings-privacy-macros { #settings-channel-menu-end { clear: both; margin-bottom: 15px; -}
\ No newline at end of file +} diff --git a/view/css/mod_thing.css b/view/css/mod_thing.css index 125230b38..ddb2faa87 100644 --- a/view/css/mod_thing.css +++ b/view/css/mod_thing.css @@ -4,8 +4,9 @@ margin-left: 0; } - -.thing-label, .field label, .thing-verb-label, .thing-profile-label{ +/* first-of-type needed to style switches */ +.field label.mainlabel, +.thing-label, .field label:first-of-type, .thing-verb-label, .thing-profile-label{ float: left; width: 350px; } @@ -18,4 +19,4 @@ .thing-field-end { clear: both; -}
\ No newline at end of file +} diff --git a/view/js/mod_admin.js b/view/js/mod_admin.js new file mode 100644 index 000000000..aad2ca902 --- /dev/null +++ b/view/js/mod_admin.js @@ -0,0 +1,3 @@ +$(document).ready(function() { + $('form').areYouSure(); // Warn user about unsaved settings +}); diff --git a/view/js/mod_new_channel.js b/view/js/mod_new_channel.js index 492267ff9..c4d5408f2 100644 --- a/view/js/mod_new_channel.js +++ b/view/js/mod_new_channel.js @@ -1,5 +1,5 @@ $(document).ready(function() { -// $("#privacy-role-select").sSelect(); +// $("#id_permissions_role").sSelect(); $("#newchannel-name").blur(function() { $("#name-spinner").spin('small'); var zreg_name = $("#newchannel-name").val(); diff --git a/view/js/mod_profiles.js b/view/js/mod_profiles.js new file mode 100644 index 000000000..aad2ca902 --- /dev/null +++ b/view/js/mod_profiles.js @@ -0,0 +1,3 @@ +$(document).ready(function() { + $('form').areYouSure(); // Warn user about unsaved settings +}); diff --git a/view/js/mod_settings.js b/view/js/mod_settings.js index 87c8c3a2b..0db0dd165 100644 --- a/view/js/mod_settings.js +++ b/view/js/mod_settings.js @@ -2,14 +2,15 @@ var ispublic = aStr['everybody'] ; $(document).ready(function() { + $('form').areYouSure(); // Warn user about unsaved settings $("a#settings-default-perms-menu").colorbox({ 'inline' : true, 'transition' : 'elastic' }); - $("#privacy-role-select").change(function() { - var role = $("#privacy-role-select").val(); + $("#id_permissions_role").change(function() { + var role = $("#id_permissions_role").val(); if(role == 'custom') $('#advanced-perm').show(); else diff --git a/view/php/theme_init.php b/view/php/theme_init.php index f28f9aa8d..33b7e87dc 100644 --- a/view/php/theme_init.php +++ b/view/php/theme_init.php @@ -44,6 +44,7 @@ head_add_js('docready.js'); head_add_js('library/colorbox/jquery.colorbox-min.js'); head_add_js('library/bootstrap-tagsinput/bootstrap-tagsinput.js'); +head_add_js('library/jquery.AreYouSure/jquery.are-you-sure.js'); /** * Those who require this feature will know what to do with it. * Those who don't, won't. diff --git a/view/theme/redbasic/css/style.css b/view/theme/redbasic/css/style.css index 60760509d..61acc8ec7 100644 --- a/view/theme/redbasic/css/style.css +++ b/view/theme/redbasic/css/style.css @@ -301,14 +301,14 @@ footer { #main-login #id_remember { float: left; padding: 0; - margin-top: 15px; margin-bottom: 0; margin-left: 0; width: 20px; } -#main-login .field.checkbox label { - margin-top: 15px; +/* first-of-type needed to style switches */ +#main-login .field.checkbox label.mainlabel, +#main-login .field.checkbox label:first-of-type { margin-bottom: 0; float: left; width: 100px; @@ -373,13 +373,16 @@ footer { font-weight: bold; } -#profile-edit-wrapper .field label { +#profile-edit-wrapper .field { margin-top: 20px; +} +/* first-of-type needed to style switches */ +#profile-edit-wrapper .field label.mainlabel, +#profile-edit-wrapper .field label:first-of-type { width: 175px; } #profile-edit-wrapper .field input[type="text"] { - margin-top: 20px; width: 220px; } @@ -1242,7 +1245,9 @@ footer { width: 100% } -.field label { +/* first-of-type needed to style switches */ +.field label.mainlabel, +.field label:first-of-type { float: left; width: 350px; } @@ -1296,6 +1301,8 @@ footer { .hidden { display: none!important; } .field.radio .field_help { margin-left: 0px; } +.field.checkbox .field_help { display: inline; margin-left: 10px; } + /** * OAuth @@ -2350,3 +2357,70 @@ aside .nav > li > a:hover, aside .nav > li > a:focus { .jothidden .bootstrap-tagsinput:hover, .jothidden .bootstrap-tagsinput:focus { border: 1px solid #cccccc; } + +/* Hide the placeholder label which is used for styling switches */ +/* Many places give a width to all labels, so need to specifically set these to 0 width */ +/* This should probably be moved to core */ +.field.checkbox label.switchlabel, +.field.checkbox label:nth-of-type(2) { + width: 0px; + margin: 0px; + float: none; +} + +/* Turn checkboxes into switches */ +/* Doesn't work with IE<9. */ +.field.checkbox input { + position: absolute; + margin-left: -9999px; + visibility: hidden; +} +.field.checkbox input + label.switchlabel, +.field.checkbox input + label:nth-of-type(2) { + display: block; + position: relative; + cursor: pointer; + outline: none; + user-select: none; + + padding: 2px; + width: 60px; + height: 24px; + background-color: #dddddd; + border-radius: 60px; + transition: background 0.4s; + + float:left; +} + +.field.checkbox input + label:before, +.field.checkbox input + label:after { + display: block; + position: absolute; + content: ""; +} +.field.checkbox input + label:before { + top: 2px; + left: 2px; + bottom: 2px; + right: 2px; + background-color: #fff; + border-radius: 30px; + transition: background 0.4s; +} +.field.checkbox input + label:after { + top: 4px; + left: 4px; + bottom: 4px; + width: 30px; + background-color: #dddddd; + border-radius: 30px; + transition: margin 0.4s, background 0.4s; +} +.field.checkbox input:checked + label { + background-color: #8ce196; +} +.field.checkbox input:checked + label:after { + margin-left: 22px; + background-color: #8ce196; +} diff --git a/view/tpl/field_acheckbox.tpl b/view/tpl/field_acheckbox.tpl index 89de170b7..816af2a65 100755 --- a/view/tpl/field_acheckbox.tpl +++ b/view/tpl/field_acheckbox.tpl @@ -1,6 +1,6 @@ <tr> <td> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> </td> <td class="abook-them"> <input type="checkbox" name='them_{{$field.0}}' id='them_id_{{$field.0}}' value="1" disabled="disabled" {{if $field.2}}checked="checked"{{/if}} /> diff --git a/view/tpl/field_checkbox.tpl b/view/tpl/field_checkbox.tpl index 51d56f69c..5c7f58ad1 100755 --- a/view/tpl/field_checkbox.tpl +++ b/view/tpl/field_checkbox.tpl @@ -1,5 +1,4 @@ <div class='field checkbox'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> - <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}}> - <span class='field_help'>{{$field.3}}</span> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> + <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="1" {{if $field.2}}checked="checked"{{/if}}><label class="switchlabel" for='id_{{$field.0}}'></label><span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_colorinput.tpl b/view/tpl/field_colorinput.tpl index a68781698..a1e912186 100644 --- a/view/tpl/field_colorinput.tpl +++ b/view/tpl/field_colorinput.tpl @@ -1,5 +1,5 @@ <div class='field input color'> - <label for='id_{{$field.0}}' id='label_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}' id='label_{{$field.0}}'>{{$field.1}}</label> <input class='color' name='{{$field.0}}' id='id_{{$field.0}}' type="text" value="{{$field.2}}">{{if $field.4}} <span class="required">{{$field.4}}</span> {{/if}} <span id='help_{{$field.0}}' class='field_help'>{{$field.3}}</span> <div id='end_{{$field.0}}' class='field_end'></div> diff --git a/view/tpl/field_combobox.tpl b/view/tpl/field_combobox.tpl index 1f9218954..337c60673 100755 --- a/view/tpl/field_combobox.tpl +++ b/view/tpl/field_combobox.tpl @@ -1,5 +1,5 @@ <div class='field combobox'> - <label for='id_{{$field.0}}' id='id_{{$field.0}}_label'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}' id='id_{{$field.0}}_label'>{{$field.1}}</label> {{* html5 don't work on Chrome, Safari and IE9 see https://github.com/thgreasi/datalist-polyfill <input id="id_{{$field.0}}" type="text" list="data_{{$field.0}}" > <datalist id="data_{{$field.0}}" > diff --git a/view/tpl/field_custom.tpl b/view/tpl/field_custom.tpl index a6b49f6da..754f5b2f4 100755 --- a/view/tpl/field_custom.tpl +++ b/view/tpl/field_custom.tpl @@ -1,5 +1,5 @@ <div class='field custom'> - <label for='{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='{{$field.0}}'>{{$field.1}}</label> {{$field.2}} <span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_input.tpl b/view/tpl/field_input.tpl index a584f95e7..be6e3f047 100755 --- a/view/tpl/field_input.tpl +++ b/view/tpl/field_input.tpl @@ -1,5 +1,5 @@ <div class='field input'> - <label for='id_{{$field.0}}' id='label_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}' id='label_{{$field.0}}'>{{$field.1}}</label> <input name='{{$field.0}}' id='id_{{$field.0}}' type="text" value="{{$field.2}}">{{if $field.4}} <span class="required">{{$field.4}}</span> {{/if}} <span id='help_{{$field.0}}' class='field_help'>{{$field.3}}</span> <div id='end_{{$field.0}}' class='field_end'></div> diff --git a/view/tpl/field_intcheckbox.tpl b/view/tpl/field_intcheckbox.tpl index d9a8d7289..1d0bd9175 100755 --- a/view/tpl/field_intcheckbox.tpl +++ b/view/tpl/field_intcheckbox.tpl @@ -1,5 +1,4 @@ <div class='field checkbox'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> - <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3}}" {{if $field.2}}checked="true"{{/if}}> - <span class='field_help'>{{$field.4}}</span> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> + <input type="checkbox" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.3}}" {{if $field.2}}checked="true"{{/if}}><label class="switchlabel" for='id_{{$field.0}}'></label><span class='field_help'>{{$field.4}}</span> </div> diff --git a/view/tpl/field_password.tpl b/view/tpl/field_password.tpl index 23058f8a6..38ecf3d07 100755 --- a/view/tpl/field_password.tpl +++ b/view/tpl/field_password.tpl @@ -1,5 +1,5 @@ <div class='field password'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <input type='password' name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2}}"> <span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_radio.tpl b/view/tpl/field_radio.tpl index 147b6b834..a92324cad 100755 --- a/view/tpl/field_radio.tpl +++ b/view/tpl/field_radio.tpl @@ -1,5 +1,5 @@ <div class='field radio'> - <label for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}_{{$field.2}}'>{{$field.1}}</label> <input type="radio" name='{{$field.0}}' id='id_{{$field.0}}_{{$field.2}}' value="{{$field.2}}" {{if $field.4}}checked="true"{{/if}}> <span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_richtext.tpl b/view/tpl/field_richtext.tpl index c8639cf10..378e02a62 100755 --- a/view/tpl/field_richtext.tpl +++ b/view/tpl/field_richtext.tpl @@ -1,5 +1,5 @@ <div class='field richtext'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <textarea name='{{$field.0}}' id='id_{{$field.0}}' class="fieldRichtext">{{$field.2}}</textarea> <span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_select.tpl b/view/tpl/field_select.tpl index 9aca26e7b..95d1855d6 100755 --- a/view/tpl/field_select.tpl +++ b/view/tpl/field_select.tpl @@ -1,5 +1,5 @@ <div class='field select'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <select name='{{$field.0}}' id='id_{{$field.0}}'> {{foreach $field.4 as $opt=>$val}}<option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} </select> diff --git a/view/tpl/field_select_disabled.tpl b/view/tpl/field_select_disabled.tpl index f0090cf98..e241be895 100644 --- a/view/tpl/field_select_disabled.tpl +++ b/view/tpl/field_select_disabled.tpl @@ -1,5 +1,5 @@ <div class='field select'> - <label style="font-weight: normal;" for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" style="font-weight: normal;" for='id_{{$field.0}}'>{{$field.1}}</label> <select disabled="true" name='{{$field.0}}' id='id_{{$field.0}}'> {{foreach $field.4 as $opt=>$val}}<option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} </select> diff --git a/view/tpl/field_select_grouped.tpl b/view/tpl/field_select_grouped.tpl new file mode 100644 index 000000000..c7fb4f322 --- /dev/null +++ b/view/tpl/field_select_grouped.tpl @@ -0,0 +1,12 @@ + <div class='field select'> + <label for='id_{{$field.0}}'>{{$field.1}}</label> + <select name='{{$field.0}}' id='id_{{$field.0}}'> + {{foreach $field.4 as $group=>$opts}} + <optgroup label='{{$group}}'> + {{foreach $opts as $opt=>$val}} + <option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} + {{/foreach}} + </optgroup> + </select> + <span class='field_help'>{{$field.3}}</span> + </div> diff --git a/view/tpl/field_select_raw.tpl b/view/tpl/field_select_raw.tpl index 861be3201..74d575bd0 100755 --- a/view/tpl/field_select_raw.tpl +++ b/view/tpl/field_select_raw.tpl @@ -1,5 +1,5 @@ <div class='field select'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <select name='{{$field.0}}' id='id_{{$field.0}}'> {{$field.4}} </select> diff --git a/view/tpl/field_textarea.tpl b/view/tpl/field_textarea.tpl index b454045c0..dad89a145 100755 --- a/view/tpl/field_textarea.tpl +++ b/view/tpl/field_textarea.tpl @@ -1,5 +1,5 @@ <div class='field textarea'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <textarea name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.4}}{{$field.4}}{{/if}} >{{$field.2}}</textarea> <span class='field_help'>{{$field.3}}</span> </div> diff --git a/view/tpl/field_themeselect.tpl b/view/tpl/field_themeselect.tpl index a0e454bf5..120727478 100755 --- a/view/tpl/field_themeselect.tpl +++ b/view/tpl/field_themeselect.tpl @@ -1,6 +1,6 @@ <script>$(document).ready(function(){ previewTheme($("#id_{{$field.0}}")[0]); });</script> <div class='field select'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <select name='{{$field.0}}' id='id_{{$field.0}}' {{if $field.5=='preview'}}onchange="previewTheme(this);"{{/if}} > {{foreach $field.4 as $opt=>$val}}<option value="{{$opt}}" {{if $opt==$field.2}}selected="selected"{{/if}}>{{$val}}</option>{{/foreach}} </select> diff --git a/view/tpl/field_yesno.tpl b/view/tpl/field_yesno.tpl index e36e775c7..f5a909833 100755 --- a/view/tpl/field_yesno.tpl +++ b/view/tpl/field_yesno.tpl @@ -1,5 +1,5 @@ <div class='field yesno'> - <label for='id_{{$field.0}}'>{{$field.1}}</label> + <label class="mainlabel" for='id_{{$field.0}}'>{{$field.1}}</label> <div class='onoff' id="id_{{$field.0}}_onoff"> <input type="hidden" name='{{$field.0}}' id='id_{{$field.0}}' value="{{$field.2}}"> <a href="#" class='off'> diff --git a/view/tpl/install_settings.tpl b/view/tpl/install_settings.tpl index f4fd82fdb..62dcbb8b3 100755 --- a/view/tpl/install_settings.tpl +++ b/view/tpl/install_settings.tpl @@ -20,7 +20,7 @@ {{include file="field_input.tpl" field=$adminmail}} {{include file="field_input.tpl" field=$siteurl}} -{{$timezone}} +{{include file="field_select_grouped.tpl" field=$timezone}} <input id="install-submit" type="submit" name="submit" value="{{$submit}}" /> diff --git a/view/tpl/new_channel.tpl b/view/tpl/new_channel.tpl index 241846eb4..ff2011181 100755 --- a/view/tpl/new_channel.tpl +++ b/view/tpl/new_channel.tpl @@ -5,10 +5,7 @@ <div id="newchannel-desc" class="descriptive-paragraph">{{$desc}}</div> <div id="newchannel-role-help" class="descriptive-paragraph">{{$help_role}}</div> - - <label for="newchannel-role" id="label-newchannel-role" class="newchannel-label" >{{$label_role}}</label> - {{$role_select}} - <div class="newchannel-role-morehelp"><a href="help/roles" title="{{$what_is_role}}" target="_blank">{{$questionmark}}</a></div> + {{include file="field_select_grouped.tpl" field=$role}} <div id="newchannel-role-end" class="newchannel-field-end"></div> diff --git a/view/tpl/profile_hide_friends.tpl b/view/tpl/profile_hide_friends.tpl index 1d748cd4e..4ed6782bb 100644 --- a/view/tpl/profile_hide_friends.tpl +++ b/view/tpl/profile_hide_friends.tpl @@ -1,3 +1,6 @@ +{{include file="field_checkbox.tpl"}} + +{{* <p id="hide-friends-text"> {{$desc}} </p> @@ -14,4 +17,4 @@ <div id="hide-friends-end"></div> </div> - +*}} diff --git a/view/tpl/select_timezone.tpl b/view/tpl/select_timezone.tpl new file mode 100644 index 000000000..2820a54f4 --- /dev/null +++ b/view/tpl/select_timezone.tpl @@ -0,0 +1,11 @@ +{{* TODO: Make id configurabel *}} +<select id='timezone_select' name='timezone'> +{{foreach $continents as $continent => $cities}} +<optgroup label="{{$continent}}"> +{{foreach $cities as $city => $value}} +<option value='{{$value}}' {{if $value == $selected}}selected='selected'{{/if}}>{{$city}}</option> +{{/foreach}} +</optgroup> +{{/foreach}} +</select> + diff --git a/view/tpl/settings.tpl b/view/tpl/settings.tpl index 47d85d8e4..894f0fe83 100755 --- a/view/tpl/settings.tpl +++ b/view/tpl/settings.tpl @@ -9,7 +9,7 @@ <h3 class="settings-heading">{{$h_basic}}</h3> {{include file="field_input.tpl" field=$username}} -{{include file="field_custom.tpl" field=$timezone}} +{{include file="field_select_grouped.tpl" field=$timezone}} {{include file="field_input.tpl" field=$defloc}} {{include file="field_checkbox.tpl" field=$allowloc}} @@ -22,10 +22,7 @@ <h3 class="settings-heading">{{$h_prv}}</h3> -<div class="field custom"> -<label for="privacy-role-select">{{$role_lbl}}</label> -{{$role_select}} -</div> +{{include file="field_select_grouped.tpl" field=$role}} <div id="advanced-perm" style="display:{{if $permissions_set}}none{{else}}block{{/if}};"> {{include file="field_checkbox.tpl" field=$hide_presence}} @@ -62,7 +59,7 @@ {{$suggestme}} -{{include file="field_yesno.tpl" field=$blocktags}} +{{include file="field_checkbox.tpl" field=$blocktags}} {{include file="field_input.tpl" field=$expire}} diff --git a/view/tpl/settings_features.tpl b/view/tpl/settings_features.tpl index 3291162fa..3ede4c76d 100755 --- a/view/tpl/settings_features.tpl +++ b/view/tpl/settings_features.tpl @@ -9,7 +9,7 @@ <h3 class="settings-heading">{{$f.0}}</h3> {{foreach $f.1 as $fcat}} - {{include file="{{$field_yesno}}" field=$fcat}} + {{include file="field_checkbox.tpl" field=$fcat}} {{/foreach}} {{/foreach}} |