2014-04-22 14:29:35 +00:00
|
|
|
/*!
|
2014-05-31 14:03:58 +00:00
|
|
|
* stickyscroll
|
|
|
|
* https://github.com/erming/stickyscroll
|
2014-04-22 14:29:35 +00:00
|
|
|
*
|
|
|
|
* Copyright (c) 2014 Mattias Erming <mattias@mattiaserming.com>
|
|
|
|
* Licensed under the MIT License.
|
|
|
|
*
|
2014-05-31 14:03:58 +00:00
|
|
|
* Version 1.3.1
|
2014-04-22 14:29:35 +00:00
|
|
|
*/
|
|
|
|
(function($) {
|
2014-05-24 19:45:28 +00:00
|
|
|
$.fn.sticky = function(options) {
|
2014-04-22 14:29:35 +00:00
|
|
|
var settings = $.extend({
|
2014-04-28 18:05:19 +00:00
|
|
|
disableManualScroll: false,
|
2014-05-31 14:03:58 +00:00
|
|
|
overflow: 'auto',
|
2014-04-23 18:44:44 +00:00
|
|
|
scrollToBottom: true,
|
2014-04-28 18:05:19 +00:00
|
|
|
speed: 0
|
2014-04-22 14:29:35 +00:00
|
|
|
}, options);
|
2014-05-03 17:21:11 +00:00
|
|
|
|
2014-04-22 14:29:35 +00:00
|
|
|
var self = this;
|
|
|
|
if (self.size() > 1) {
|
|
|
|
return self.each(function() {
|
2014-05-24 19:45:28 +00:00
|
|
|
$(this).sticky(options);
|
2014-04-22 14:29:35 +00:00
|
|
|
});
|
|
|
|
}
|
2014-04-23 18:44:44 +00:00
|
|
|
|
2014-05-03 17:21:11 +00:00
|
|
|
self.css('overflow-y', settings.overflow);
|
2014-05-31 14:03:58 +00:00
|
|
|
self.css('-webkit-overflow-scrolling', 'touch');
|
2014-04-23 18:44:44 +00:00
|
|
|
if (settings.scrollToBottom) {
|
|
|
|
self.scrollToBottom();
|
|
|
|
}
|
2014-05-03 17:21:11 +00:00
|
|
|
|
2014-05-31 14:03:58 +00:00
|
|
|
var resizeTimer;
|
2014-05-03 17:21:11 +00:00
|
|
|
var resizing = false;
|
2014-04-22 14:29:35 +00:00
|
|
|
$(window).on('resize', function() {
|
|
|
|
self.finish();
|
2014-05-03 17:21:11 +00:00
|
|
|
|
|
|
|
// This will prevent the scroll event from triggering
|
|
|
|
// while resizing the browser.
|
|
|
|
resizing = true;
|
|
|
|
|
2014-05-31 14:03:58 +00:00
|
|
|
clearTimeout(resizeTimer);
|
|
|
|
resizeTimer = setTimeout(function() {
|
2014-05-03 17:21:11 +00:00
|
|
|
resizing = false;
|
|
|
|
}, 100);
|
|
|
|
|
|
|
|
if (sticky) {
|
|
|
|
self.scrollToBottom();
|
|
|
|
}
|
2014-04-22 14:29:35 +00:00
|
|
|
});
|
2014-05-03 17:21:11 +00:00
|
|
|
|
2014-05-31 14:03:58 +00:00
|
|
|
var scrollTimer;
|
2014-04-28 18:05:19 +00:00
|
|
|
var sticky = true;
|
2014-04-22 14:29:35 +00:00
|
|
|
self.on('scroll', function() {
|
2014-04-28 18:05:19 +00:00
|
|
|
if (settings.disableManualScroll) {
|
|
|
|
self.scrollToBottom();
|
2014-05-03 17:21:11 +00:00
|
|
|
} else if (!resizing) {
|
2014-05-31 14:03:58 +00:00
|
|
|
clearTimeout(scrollTimer);
|
|
|
|
scrollTimer = setTimeout(function() {
|
|
|
|
sticky = self.isScrollAtBottom();
|
|
|
|
}, 50);
|
2014-04-28 18:05:19 +00:00
|
|
|
}
|
2014-04-22 14:29:35 +00:00
|
|
|
});
|
|
|
|
self.trigger('scroll');
|
2014-05-03 17:21:11 +00:00
|
|
|
self.on('prepend append', function() {
|
2014-04-22 14:29:35 +00:00
|
|
|
if (sticky) {
|
|
|
|
self.scrollToBottom(settings.speed);
|
|
|
|
}
|
|
|
|
});
|
2014-05-03 17:21:11 +00:00
|
|
|
|
2014-04-22 14:29:35 +00:00
|
|
|
return this;
|
|
|
|
};
|
2014-04-24 18:11:45 +00:00
|
|
|
|
2014-05-03 17:21:11 +00:00
|
|
|
// Normally, these functions won't trigger any events.
|
|
|
|
// Lets override them.
|
|
|
|
var events = ['prepend', 'append'];
|
|
|
|
$.each(events, function(i, e) {
|
|
|
|
var fn = $.fn[e];
|
|
|
|
$.fn[e] = function() {
|
|
|
|
return fn.apply(this, arguments).trigger(e);
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
$.fn.isScrollAtBottom = function() {
|
|
|
|
if ((this.scrollTop() + this.outerHeight() + 1) >= this.prop('scrollHeight')) {
|
|
|
|
return true;
|
2014-04-22 14:29:35 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
$.fn.scrollToBottom = function(speed) {
|
|
|
|
return this.each(function() {
|
|
|
|
$(this).finish().animate({scrollTop: this.scrollHeight}, speed || 0);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
})(jQuery);
|
|
|
|
|
|
|
|
/*!
|
2014-06-06 20:05:47 +00:00
|
|
|
* tabcomplete
|
|
|
|
* Lightweight tab completion for inputs and textareas
|
2014-05-24 19:45:28 +00:00
|
|
|
*
|
|
|
|
* Source:
|
2014-06-06 20:05:47 +00:00
|
|
|
* https://github.com/erming/tabcomplete
|
2014-04-22 14:29:35 +00:00
|
|
|
*
|
|
|
|
* Copyright (c) 2014 Mattias Erming <mattias@mattiaserming.com>
|
|
|
|
* Licensed under the MIT License.
|
|
|
|
*
|
2014-06-06 20:05:47 +00:00
|
|
|
* Version 1.3.1
|
2014-04-22 14:29:35 +00:00
|
|
|
*/
|
|
|
|
(function($) {
|
2014-05-24 19:45:28 +00:00
|
|
|
var keys = {
|
2014-06-06 20:05:47 +00:00
|
|
|
backspace: 8,
|
2014-05-24 19:45:28 +00:00
|
|
|
tab: 9,
|
|
|
|
up: 38,
|
|
|
|
down: 40
|
2014-05-13 22:47:35 +00:00
|
|
|
};
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
$.fn.tab = // Alias
|
|
|
|
$.fn.tabcomplete = function(args, options) {
|
2014-05-13 22:47:35 +00:00
|
|
|
if (this.length > 1) {
|
|
|
|
return this.each(function() {
|
2014-06-06 20:05:47 +00:00
|
|
|
$(this).tabcomplete(args, options);
|
2014-04-22 14:29:35 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
// Only enable the plugin on <input> and <textarea> elements.
|
|
|
|
var tag = this.prop("tagName");
|
|
|
|
if (tag != "INPUT" && tag != "TEXTAREA") {
|
|
|
|
return;
|
2014-05-13 22:47:35 +00:00
|
|
|
}
|
2014-04-22 14:29:35 +00:00
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
// Set default options.
|
|
|
|
options = $.extend({
|
|
|
|
after: "",
|
|
|
|
arrowKeys: tag == "INPUT" ? true : false,
|
|
|
|
caseSensitive: false,
|
2014-06-06 20:05:47 +00:00
|
|
|
hint: "placeholder",
|
|
|
|
minLength: 1
|
2014-05-24 19:45:28 +00:00
|
|
|
}, options);
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Remove any leftovers.
|
2014-05-13 22:47:35 +00:00
|
|
|
// This allows us to override the plugin if necessary.
|
2014-06-06 20:05:47 +00:00
|
|
|
this.unbind(".tabcomplete");
|
|
|
|
this.prev(".hint").remove();
|
2014-05-13 22:47:35 +00:00
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
var self = this;
|
2014-06-06 20:05:47 +00:00
|
|
|
var backspace = false;
|
2014-05-24 19:45:28 +00:00
|
|
|
var i = -1;
|
2014-05-13 22:47:35 +00:00
|
|
|
var words = [];
|
|
|
|
var last = "";
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
var hint = $.noop;
|
|
|
|
|
|
|
|
// Determine what type of hinting to use.
|
|
|
|
switch (options.hint) {
|
|
|
|
case "placeholder":
|
|
|
|
hint = placeholder;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case "select":
|
|
|
|
hint = select;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.on("input.tabcomplete", function() {
|
2014-05-13 22:47:35 +00:00
|
|
|
var input = self.val();
|
|
|
|
var word = input.split(/ |\n/).pop();
|
2014-04-22 14:29:35 +00:00
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Reset iteration.
|
|
|
|
i = -1;
|
|
|
|
last = "";
|
|
|
|
words = [];
|
|
|
|
|
|
|
|
// Check for matches if the current word is the last word.
|
|
|
|
if (self[0].selectionStart == input.length
|
|
|
|
&& word.length) {
|
|
|
|
if (typeof args === "function") {
|
|
|
|
// If the user supplies a function, invoke it
|
|
|
|
// and keep the result.
|
|
|
|
words = args(word);
|
|
|
|
} else {
|
|
|
|
// Otherwise, call the .match() function.
|
|
|
|
words = match(word, args, options.caseSensitive);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append 'after' to each word.
|
|
|
|
if (options.after) {
|
|
|
|
words = $.map(words, function(w) { return w + options.after; });
|
|
|
|
}
|
2014-04-22 14:29:35 +00:00
|
|
|
}
|
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
// Emit the number of matching words with the 'match' event.
|
|
|
|
self.trigger("match", words.length);
|
|
|
|
|
2014-05-13 22:47:35 +00:00
|
|
|
if (options.hint) {
|
2014-06-06 20:05:47 +00:00
|
|
|
if (!(options.hint == "select" && backspace) && word.length >= options.minLength) {
|
|
|
|
// Show hint.
|
2014-05-13 22:47:35 +00:00
|
|
|
hint.call(self, words[0]);
|
|
|
|
} else {
|
|
|
|
// Clear hinting.
|
|
|
|
// This call is needed when using backspace.
|
|
|
|
hint.call(self, "");
|
|
|
|
}
|
|
|
|
}
|
2014-06-06 20:05:47 +00:00
|
|
|
|
|
|
|
if (backspace) {
|
|
|
|
backspace = false;
|
|
|
|
}
|
2014-05-13 22:47:35 +00:00
|
|
|
});
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
this.on("keydown.tabcomplete", function(e) {
|
2014-05-13 22:47:35 +00:00
|
|
|
var key = e.which;
|
2014-06-06 20:05:47 +00:00
|
|
|
if (key == keys.tab
|
|
|
|
|| (options.arrowKeys && (key == keys.up || key == keys.down))) {
|
|
|
|
|
2014-05-13 22:47:35 +00:00
|
|
|
// Don't lose focus on tab click.
|
|
|
|
e.preventDefault();
|
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
// Iterate the matches with tab and the up and down keys by incrementing
|
|
|
|
// or decrementing the 'i' variable.
|
|
|
|
if (key != keys.up) {
|
|
|
|
i++;
|
|
|
|
} else {
|
|
|
|
if (i == -1) return;
|
|
|
|
if (i == 0) {
|
|
|
|
// Jump to the last word.
|
|
|
|
i = words.length - 1;
|
|
|
|
} else {
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
2014-06-06 20:05:47 +00:00
|
|
|
|
2014-05-13 22:47:35 +00:00
|
|
|
// Get next match.
|
2014-05-24 19:45:28 +00:00
|
|
|
var word = words[i % words.length];
|
2014-05-13 22:47:35 +00:00
|
|
|
if (!word) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
var value = self.val();
|
|
|
|
last = last || value.split(/ |\n/).pop();
|
2014-05-13 22:47:35 +00:00
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Return if the 'minLength' requirement isn't met.
|
2014-05-13 22:47:35 +00:00
|
|
|
if (last.length < options.minLength) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Update element with the completed text.
|
|
|
|
var text = value.substr(0, self[0].selectionStart - last.length) + word;
|
|
|
|
self.val(text);
|
|
|
|
|
|
|
|
// Put the cursor at the end after completion.
|
|
|
|
// This isn't strictly necessary, but solves an issue with
|
|
|
|
// Internet Explorer.
|
|
|
|
if (options.hint == "select") {
|
|
|
|
self[0].selectionStart = text.length;
|
|
|
|
}
|
2014-05-13 22:47:35 +00:00
|
|
|
|
|
|
|
// Remember the word until next time.
|
|
|
|
last = word;
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Emit event.
|
|
|
|
self.trigger("tabcomplete", last);
|
2014-05-24 19:45:28 +00:00
|
|
|
|
2014-05-13 22:47:35 +00:00
|
|
|
if (options.hint) {
|
|
|
|
// Turn off any additional hinting.
|
|
|
|
hint.call(self, "");
|
|
|
|
}
|
2014-06-06 20:05:47 +00:00
|
|
|
} else if (e.which == keys.backspace) {
|
|
|
|
// Remember that backspace was pressed. This is used
|
|
|
|
// by the 'input' event.
|
|
|
|
backspace = true;
|
|
|
|
|
|
|
|
// Reset iteration.
|
|
|
|
i = -1;
|
|
|
|
last = "";
|
2014-04-22 14:29:35 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-05-24 19:45:28 +00:00
|
|
|
if (options.hint) {
|
|
|
|
// If enabled, turn on hinting.
|
|
|
|
hint.call(this, "");
|
|
|
|
}
|
|
|
|
|
2014-04-22 14:29:35 +00:00
|
|
|
return this;
|
2014-05-13 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Simple matching.
|
2014-06-06 20:05:47 +00:00
|
|
|
// Filter the array and return the items that begins with 'word'.
|
|
|
|
function match(word, array, caseSensitive) {
|
2014-05-13 22:47:35 +00:00
|
|
|
return $.grep(
|
|
|
|
array,
|
|
|
|
function(w) {
|
|
|
|
if (caseSensitive) {
|
|
|
|
return !w.indexOf(word);
|
|
|
|
} else {
|
|
|
|
return !w.toLowerCase().indexOf(word.toLowerCase());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2014-06-06 20:05:47 +00:00
|
|
|
// Show placeholder text.
|
2014-05-13 22:47:35 +00:00
|
|
|
// This works by creating a copy of the input and placing it behind
|
|
|
|
// the real input.
|
2014-06-06 20:05:47 +00:00
|
|
|
function placeholder(word) {
|
2014-05-13 22:47:35 +00:00
|
|
|
var input = this;
|
|
|
|
var clone = input.prev(".hint");
|
|
|
|
|
|
|
|
input.css({
|
|
|
|
backgroundColor: "transparent",
|
|
|
|
position: "relative",
|
|
|
|
});
|
|
|
|
|
|
|
|
// Lets create a clone of the input if it does
|
|
|
|
// not already exist.
|
|
|
|
if (!clone.length) {
|
|
|
|
input.wrap(
|
|
|
|
$("<div>").css({position: "relative"})
|
|
|
|
);
|
|
|
|
clone = input
|
|
|
|
.clone()
|
2014-05-24 19:45:28 +00:00
|
|
|
.attr("tabindex", -1)
|
2014-05-13 22:47:35 +00:00
|
|
|
.removeAttr("id name placeholder")
|
|
|
|
.addClass("hint")
|
|
|
|
.insertBefore(input);
|
|
|
|
clone.css({
|
|
|
|
position: "absolute",
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
var hint = "";
|
|
|
|
if (typeof word !== "undefined") {
|
2014-06-06 20:05:47 +00:00
|
|
|
var value = input.val();
|
|
|
|
hint = value + word.substr(value.split(/ |\n/).pop().length);
|
2014-05-13 22:47:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
clone.val(hint);
|
|
|
|
}
|
2014-06-06 20:05:47 +00:00
|
|
|
|
|
|
|
// Hint by selecting part of the suggested word.
|
|
|
|
function select(word) {
|
|
|
|
var input = this;
|
|
|
|
var value = input.val();
|
|
|
|
if (word) {
|
|
|
|
input.val(
|
|
|
|
value
|
|
|
|
+ word.substr(value.split(/ |\n/).pop().length)
|
|
|
|
);
|
|
|
|
|
|
|
|
// Select hint.
|
|
|
|
input[0].selectionStart = value.length;
|
|
|
|
}
|
|
|
|
}
|
2014-04-22 14:29:35 +00:00
|
|
|
})(jQuery);
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* jQuery Cookie Plugin v1.4.0
|
|
|
|
* https://github.com/carhartl/jquery-cookie
|
|
|
|
*
|
|
|
|
* Copyright 2013 Klaus Hartl
|
|
|
|
* Released under the MIT license
|
|
|
|
*/
|
|
|
|
(function (factory) {
|
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
|
|
// AMD. Register as anonymous module.
|
|
|
|
define(['jquery'], factory);
|
|
|
|
} else {
|
|
|
|
// Browser globals.
|
|
|
|
factory(jQuery);
|
|
|
|
}
|
|
|
|
}(function ($) {
|
|
|
|
|
|
|
|
var pluses = /\+/g;
|
|
|
|
|
|
|
|
function encode(s) {
|
|
|
|
return config.raw ? s : encodeURIComponent(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
function decode(s) {
|
|
|
|
return config.raw ? s : decodeURIComponent(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
function stringifyCookieValue(value) {
|
|
|
|
return encode(config.json ? JSON.stringify(value) : String(value));
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseCookieValue(s) {
|
|
|
|
if (s.indexOf('"') === 0) {
|
|
|
|
// This is a quoted cookie as according to RFC2068, unescape...
|
|
|
|
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// Replace server-side written pluses with spaces.
|
|
|
|
// If we can't decode the cookie, ignore it, it's unusable.
|
|
|
|
s = decodeURIComponent(s.replace(pluses, ' '));
|
|
|
|
} catch(e) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// If we can't parse the cookie, ignore it, it's unusable.
|
|
|
|
return config.json ? JSON.parse(s) : s;
|
|
|
|
} catch(e) {}
|
|
|
|
}
|
|
|
|
|
|
|
|
function read(s, converter) {
|
|
|
|
var value = config.raw ? s : parseCookieValue(s);
|
|
|
|
return $.isFunction(converter) ? converter(value) : value;
|
|
|
|
}
|
|
|
|
|
|
|
|
var config = $.cookie = function (key, value, options) {
|
|
|
|
|
|
|
|
// Write
|
|
|
|
if (value !== undefined && !$.isFunction(value)) {
|
|
|
|
options = $.extend({}, config.defaults, options);
|
|
|
|
|
|
|
|
if (typeof options.expires === 'number') {
|
|
|
|
var days = options.expires, t = options.expires = new Date();
|
|
|
|
t.setDate(t.getDate() + days);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (document.cookie = [
|
|
|
|
encode(key), '=', stringifyCookieValue(value),
|
|
|
|
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
|
|
|
options.path ? '; path=' + options.path : '',
|
|
|
|
options.domain ? '; domain=' + options.domain : '',
|
|
|
|
options.secure ? '; secure' : ''
|
|
|
|
].join(''));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read
|
|
|
|
|
|
|
|
var result = key ? undefined : {};
|
|
|
|
|
|
|
|
// To prevent the for loop in the first place assign an empty array
|
|
|
|
// in case there are no cookies at all. Also prevents odd result when
|
|
|
|
// calling $.cookie().
|
|
|
|
var cookies = document.cookie ? document.cookie.split('; ') : [];
|
|
|
|
|
|
|
|
for (var i = 0, l = cookies.length; i < l; i++) {
|
|
|
|
var parts = cookies[i].split('=');
|
|
|
|
var name = decode(parts.shift());
|
|
|
|
var cookie = parts.join('=');
|
|
|
|
|
|
|
|
if (key && key === name) {
|
|
|
|
// If second argument (value) is a function it's a converter...
|
|
|
|
result = read(cookie, value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prevent storing a cookie that we couldn't decode.
|
|
|
|
if (!key && (cookie = read(cookie)) !== undefined) {
|
|
|
|
result[name] = cookie;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
|
|
|
config.defaults = {};
|
|
|
|
|
|
|
|
$.removeCookie = function (key, options) {
|
|
|
|
if ($.cookie(key) !== undefined) {
|
|
|
|
// Must not alter options, thus extending a fresh object...
|
|
|
|
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
}));
|