diff --git a/Gemfile.lock b/Gemfile.lock index 2b459329b..62118728a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -141,7 +141,6 @@ GEM jquery-rails railties (>= 3.1.0) json (1.7.7) - libv8 (3.11.8.13) mail (2.4.4) i18n (>= 0.4.0) mime-types (~> 1.16) @@ -225,7 +224,6 @@ GEM rdoc (~> 3.4) thor (>= 0.14.6, < 2.0) rake (10.0.3) - rb-readline (0.4.2) rdoc (3.12.1) json (~> 1.4) redis (3.0.2) @@ -235,7 +233,6 @@ GEM chinese_pinyin (>= 0.3.0) redis (>= 2.1.1) redis-namespace (>= 1.0.2) - ref (1.0.2) resque (1.23.0) multi_json (~> 1.0) redis-namespace (~> 1.0) @@ -316,9 +313,6 @@ GEM sunspot (= 1.3.3) sunspot_solr (1.3.3) terminal-table (1.4.5) - therubyracer (0.11.3) - libv8 (~> 3.11.8.12) - ref thor (0.17.0) tilt (1.3.3) tinymce-rails (3.5.8) @@ -363,7 +357,6 @@ DEPENDENCIES jquery-rails (= 2.1.4) jquery-ui-rails kaminari! - libv8 (~> 3.11.8) mime-types mini_magick mongo_session_store-rails3 (= 3.0.6) @@ -380,7 +373,6 @@ DEPENDENCIES radius rails (~> 3.2.9) rake - rb-readline redis (>= 2.1.1) redis-namespace redis-search @@ -401,7 +393,6 @@ DEPENDENCIES sunspot-rails-tester sunspot_mongo sunspot_solr - therubyracer tinymce-rails uglifier watchr diff --git a/app/assets/javascripts/desktop.js b/app/assets/javascripts/desktop.js index c4f950550..c55534a4a 100644 --- a/app/assets/javascripts/desktop.js +++ b/app/assets/javascripts/desktop.js @@ -16,3 +16,4 @@ //= require orbitdesktop //= require jquery.gridster //= require desktop/books_pages +//= require jquery.tokeninput diff --git a/app/assets/javascripts/jquery.gridster.js b/app/assets/javascripts/jquery.gridster.js index 947f4bb2a..e8998514f 100755 --- a/app/assets/javascripts/jquery.gridster.js +++ b/app/assets/javascripts/jquery.gridster.js @@ -1328,6 +1328,7 @@ */ fn.draggable = function() { var self = this; + var draggable_options = $.extend(true, {}, this.options.draggable, { offset_left: this.options.widget_margins[0], start: function(event, ui) { @@ -3204,6 +3205,7 @@ max_rows += (+$(w).attr('data-sizey')); }); + this.cols = Math.max(min_cols, cols, this.options.min_cols); this.rows = Math.max(max_rows, this.options.min_rows); diff --git a/app/assets/javascripts/jquery.tinyscrollbar.js b/app/assets/javascripts/jquery.tinyscrollbar.js index 4bca0dfa0..fe2f2ba26 100644 --- a/app/assets/javascripts/jquery.tinyscrollbar.js +++ b/app/assets/javascripts/jquery.tinyscrollbar.js @@ -11,19 +11,20 @@ * Depends on library: jQuery * */ -( function( $ ) +;( function( $ ) { $.tiny = $.tiny || { }; $.tiny.scrollbar = { options: { - axis : 'y' // vertical or horizontal scrollbar? ( x || y ). - , wheel : 40 // how many pixels must the mouswheel scroll at a time. - , scroll : true // enable or disable the mousewheel. - , lockscroll : true // return scrollwheel to browser if there is no more content. - , size : 'auto' // set the size of the scrollbar to auto or a fixed number. - , sizethumb : 'auto' // set the size of the thumb to auto or a fixed number. - , onMove : function(){} + axis : 'y' // vertical or horizontal scrollbar? ( x || y ). + , wheel : 40 // how many pixels must the mouswheel scroll at a time. + , scroll : true // enable or disable the mousewheel. + , lockscroll : true // return scrollwheel to browser if there is no more content. + , size : 'auto' // set the size of the scrollbar to auto or a fixed number. + , sizethumb : 'auto' // set the size of the thumb to auto or a fixed number. + , invertscroll : false // Enable mobile invert style scrolling + , onMove : function(){} } }; @@ -59,13 +60,15 @@ , iScroll = 0 , iPosition = { start: 0, now: 0 } , iMouse = {} - , touchEvents = ( 'ontouchstart' in document.documentElement ) ? true : false + , touchEvents = 'ontouchstart' in document.documentElement + , UA = $.browser ; function initialize() { oSelf.update(); setEvents(); + return oSelf; } @@ -124,6 +127,9 @@ { oWrapper[0].addEventListener( 'DOMMouseScroll', wheel, false ); oWrapper[0].addEventListener( 'mousewheel', wheel, false ); + oWrapper[0].addEventListener( 'MozMousePixelScroll', function( event ){ + event.preventDefault(); + }, false); } else if( options.scroll ) { @@ -133,6 +139,16 @@ function start( event ) { + $( "body" ).addClass( "noSelect" ); + // Disable select text under IE10 + if ( UA.msie < 10 ){ + $( "body" ).attr({ + "onselectstart": "return false", + "ondragstart": "return false" + }); + } + oScrollbar.obj.addClass( "dragging" ); + var oThumbDir = parseInt( oThumb.obj.css( sDirection ), 10 ); iMouse.start = sAxis ? event.pageX : event.pageY; iPosition.start = oThumbDir == 'auto' ? 0 : oThumbDir; @@ -181,13 +197,13 @@ { if( oContent.ratio < 1 ) { - if( ! touchEvents ) + if( options.invertscroll && touchEvents ) { - iPosition.now = Math.min( ( oTrack[ options.axis ] - oThumb[ options.axis ] ), Math.max( 0, ( iPosition.start + ( ( sAxis ? event.pageX : event.pageY ) - iMouse.start)))); + iPosition.now = Math.min( ( oTrack[ options.axis ] - oThumb[ options.axis ] ), Math.max( 0, ( iPosition.start + ( iMouse.start - ( sAxis ? event.pageX : event.pageY ) )))); } else { - iPosition.now = Math.min( ( oTrack[ options.axis ] - oThumb[ options.axis ] ), Math.max( 0, ( iPosition.start + ( iMouse.start - ( sAxis ? event.pageX : event.pageY ) )))); + iPosition.now = Math.min( ( oTrack[ options.axis ] - oThumb[ options.axis ] ), Math.max( 0, ( iPosition.start + ( ( sAxis ? event.pageX : event.pageY ) - iMouse.start)))); } iScroll = iPosition.now * oScrollbar.ratio; @@ -198,6 +214,12 @@ function end() { + $( "body" ).removeClass( "noSelect" ); + // Enable select text under IE10 + if ( UA.msie < 10 ){ + $( "body" ).removeAttr( "onselectstart", "ondragstart" ); + } + oScrollbar.obj.removeClass( "dragging" ); $( document ).unbind( 'mousemove', drag ); $( document ).unbind( 'mouseup', end ); oThumb.obj.unbind( 'mouseup', end ); diff --git a/app/assets/javascripts/jquery.tokeninput.js b/app/assets/javascripts/jquery.tokeninput.js new file mode 100644 index 000000000..830e982f8 --- /dev/null +++ b/app/assets/javascripts/jquery.tokeninput.js @@ -0,0 +1,1043 @@ +/* + * jQuery Plugin: Tokenizing Autocomplete Text Entry + * Version 1.6.1 + * + * Copyright (c) 2009 James Smith (http://loopj.com) + * Licensed jointly under the GPL and MIT licenses, + * choose which one suits your project best! + * + */ + +(function ($) { +// Default settings +var DEFAULT_SETTINGS = { + // Search settings + method: "GET", + queryParam: "q", + searchDelay: 300, + minChars: 1, + propertyToSearch: "name", + jsonContainer: null, + contentType: "json", + + // Prepopulation settings + prePopulate: null, + processPrePopulate: false, + + // Display settings + hintText: "Type in a search term", + noResultsText: "No results", + searchingText: "Searching...", + deleteText: "×", + animateDropdown: true, + placeholder: null, + theme: null, + zindex: 999, + resultsLimit: null, + + enableHTML: false, + + resultsFormatter: function(item) { + var string = item[this.propertyToSearch]; + return "
  • " + (this.enableHTML ? string : _escapeHTML(string)) + "
  • "; + }, + + tokenFormatter: function(item) { + var string = item[this.propertyToSearch]; + return "
  • " + (this.enableHTML ? string : _escapeHTML(string)) + "

  • "; + }, + + // Tokenization settings + tokenLimit: null, + tokenDelimiter: ",", + preventDuplicates: false, + tokenValue: "id", + + // Behavioral settings + allowFreeTagging: false, + allowTabOut: false, + + // Callbacks + onResult: null, + onCachedResult: null, + onAdd: null, + onFreeTaggingAdd: null, + onDelete: null, + onReady: null, + + // Other settings + idPrefix: "token-input-", + + // Keep track if the input is currently in disabled mode + disabled: false +}; + +// Default classes to use when theming +var DEFAULT_CLASSES = { + tokenList: "token-input-list", + token: "token-input-token", + tokenReadOnly: "token-input-token-readonly", + tokenDelete: "token-input-delete-token", + selectedToken: "token-input-selected-token", + highlightedToken: "token-input-highlighted-token", + dropdown: "token-input-dropdown", + dropdownItem: "token-input-dropdown-item", + dropdownItem2: "token-input-dropdown-item2", + selectedDropdownItem: "token-input-selected-dropdown-item", + inputToken: "token-input-input-token", + focused: "token-input-focused", + disabled: "token-input-disabled" +}; + +// Input box position "enum" +var POSITION = { + BEFORE: 0, + AFTER: 1, + END: 2 +}; + +// Keys "enum" +var KEY = { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + ESCAPE: 27, + SPACE: 32, + PAGE_UP: 33, + PAGE_DOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + NUMPAD_ENTER: 108, + COMMA: 188 +}; + +var HTML_ESCAPES = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/' +}; + +var HTML_ESCAPE_CHARS = /[&<>"'\/]/g; + +function coerceToString(val) { + return String((val === null || val === undefined) ? '' : val); +} + +function _escapeHTML(text) { + return coerceToString(text).replace(HTML_ESCAPE_CHARS, function(match) { + return HTML_ESCAPES[match]; + }); +} + +// Additional public (exposed) methods +var methods = { + init: function(url_or_data_or_function, options) { + var settings = $.extend({}, DEFAULT_SETTINGS, options || {}); + + return this.each(function () { + $(this).data("settings", settings); + $(this).data("tokenInputObject", new $.TokenList(this, url_or_data_or_function, settings)); + }); + }, + clear: function() { + this.data("tokenInputObject").clear(); + return this; + }, + add: function(item) { + this.data("tokenInputObject").add(item); + return this; + }, + remove: function(item) { + this.data("tokenInputObject").remove(item); + return this; + }, + get: function() { + return this.data("tokenInputObject").getTokens(); + }, + toggleDisabled: function(disable) { + this.data("tokenInputObject").toggleDisabled(disable); + return this; + }, + setOptions: function(options){ + $(this).data("settings", $.extend({}, $(this).data("settings"), options || {})); + return this; + } +}; + +// Expose the .tokenInput function to jQuery as a plugin +$.fn.tokenInput = function (method) { + // Method calling and initialization logic + if(methods[method]) { + return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); + } else { + return methods.init.apply(this, arguments); + } +}; + +// TokenList class for each input +$.TokenList = function (input, url_or_data, settings) { + // + // Initialization + // + + // Configure the data source + if($.type(url_or_data) === "string" || $.type(url_or_data) === "function") { + // Set the url to query against + $(input).data("settings").url = url_or_data; + + // If the URL is a function, evaluate it here to do our initalization work + var url = computeURL(); + + // Make a smart guess about cross-domain if it wasn't explicitly specified + if($(input).data("settings").crossDomain === undefined && typeof url === "string") { + if(url.indexOf("://") === -1) { + $(input).data("settings").crossDomain = false; + } else { + $(input).data("settings").crossDomain = (location.href.split(/\/+/g)[1] !== url.split(/\/+/g)[1]); + } + } + } else if(typeof(url_or_data) === "object") { + // Set the local data to search through + $(input).data("settings").local_data = url_or_data; + } + + // Build class names + if($(input).data("settings").classes) { + // Use custom class names + $(input).data("settings").classes = $.extend({}, DEFAULT_CLASSES, $(input).data("settings").classes); + } else if($(input).data("settings").theme) { + // Use theme-suffixed default class names + $(input).data("settings").classes = {}; + $.each(DEFAULT_CLASSES, function(key, value) { + $(input).data("settings").classes[key] = value + "-" + $(input).data("settings").theme; + }); + } else { + $(input).data("settings").classes = DEFAULT_CLASSES; + } + + + // Save the tokens + var saved_tokens = []; + + // Keep track of the number of tokens in the list + var token_count = 0; + + // Basic cache to save on db hits + var cache = new $.TokenList.Cache(); + + // Keep track of the timeout, old vals + var timeout; + var input_val; + + // Create a new text input an attach keyup events + var input_box = $("") + .css({ + outline: "none" + }) + .attr("id", $(input).data("settings").idPrefix + input.id) + .focus(function () { + if ($(input).data("settings").disabled) { + return false; + } else + if ($(input).data("settings").tokenLimit === null || $(input).data("settings").tokenLimit !== token_count) { + show_dropdown_hint(); + } + token_list.addClass($(input).data("settings").classes.focused); + }) + .blur(function () { + hide_dropdown(); + + if ($(input).data("settings").allowFreeTagging) { + add_freetagging_tokens(); + } + + $(this).val(""); + token_list.removeClass($(input).data("settings").classes.focused); + }) + .bind("keyup keydown blur update", resize_input) + .keydown(function (event) { + var previous_token; + var next_token; + + switch(event.keyCode) { + case KEY.LEFT: + case KEY.RIGHT: + case KEY.UP: + case KEY.DOWN: + if(!$(this).val()) { + previous_token = input_token.prev(); + next_token = input_token.next(); + + if((previous_token.length && previous_token.get(0) === selected_token) || (next_token.length && next_token.get(0) === selected_token)) { + // Check if there is a previous/next token and it is selected + if(event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) { + deselect_token($(selected_token), POSITION.BEFORE); + } else { + deselect_token($(selected_token), POSITION.AFTER); + } + } else if((event.keyCode === KEY.LEFT || event.keyCode === KEY.UP) && previous_token.length) { + // We are moving left, select the previous token if it exists + select_token($(previous_token.get(0))); + } else if((event.keyCode === KEY.RIGHT || event.keyCode === KEY.DOWN) && next_token.length) { + // We are moving right, select the next token if it exists + select_token($(next_token.get(0))); + } + } else { + var dropdown_item = null; + + if(event.keyCode === KEY.DOWN || event.keyCode === KEY.RIGHT) { + dropdown_item = $(selected_dropdown_item).next(); + } else { + dropdown_item = $(selected_dropdown_item).prev(); + } + + if(dropdown_item.length) { + select_dropdown_item(dropdown_item); + } + } + return false; + break; + + case KEY.BACKSPACE: + previous_token = input_token.prev(); + + if(!$(this).val().length) { + if(selected_token) { + delete_token($(selected_token)); + hidden_input.change(); + } else if(previous_token.length) { + select_token($(previous_token.get(0))); + } + + return false; + } else if($(this).val().length === 1) { + hide_dropdown(); + } else { + // set a timeout just long enough to let this function finish. + setTimeout(function(){do_search();}, 5); + } + break; + + case KEY.TAB: + case KEY.ENTER: + case KEY.NUMPAD_ENTER: + case KEY.COMMA: + if(selected_dropdown_item) { + add_token($(selected_dropdown_item).data("tokeninput")); + hidden_input.change(); + } else { + if ($(input).data("settings").allowFreeTagging) { + if($(input).data("settings").allowTabOut && $(this).val() === "") { + return true; + } else { + add_freetagging_tokens(); + } + } else { + $(this).val(""); + if($(input).data("settings").allowTabOut) { + return true; + } + } + event.stopPropagation(); + event.preventDefault(); + } + return false; + + case KEY.ESCAPE: + hide_dropdown(); + return true; + + default: + if(String.fromCharCode(event.which)) { + // set a timeout just long enough to let this function finish. + setTimeout(function(){do_search();}, 5); + } + break; + } + }); + + // Keep reference for placeholder + if (settings.placeholder) + input_box.attr("placeholder", settings.placeholder) + + // Keep a reference to the original input box + var hidden_input = $(input) + .hide() + .val("") + .focus(function () { + focus_with_timeout(input_box); + }) + .blur(function () { + input_box.blur(); + }); + + // Keep a reference to the selected token and dropdown item + var selected_token = null; + var selected_token_index = 0; + var selected_dropdown_item = null; + + // The list to store the token items in + var token_list = $("