From 60ea7d51212f0fbdfca5bd583d47271f7fd746fd Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 22 Aug 2016 16:24:28 -0400 Subject: [PATCH 01/86] fixes #4966 --- app.js | 2 ++ public/src/ajaxify.js | 4 +--- public/src/utils.js | 8 ++++++++ src/controllers/authentication.js | 11 ++++++++--- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/app.js b/app.js index 2158d61282..74beb77d6e 100644 --- a/app.js +++ b/app.js @@ -103,6 +103,8 @@ function loadConfig() { nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path'))); nconf.set('core_templates_path', path.join(__dirname, 'src/views')); nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); + + nconf.set('url_parsed', url.parse(nconf.get('url'))); } diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index de20693eb0..d0592d8511 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -334,9 +334,7 @@ $(document).ready(function() { return; } - var internalLink = this.host === '' || // Relative paths are always internal links - (this.host === window.location.host && this.protocol === window.location.protocol && // Otherwise need to check if protocol and host match - (RELATIVE_PATH.length > 0 ? this.pathname.indexOf(RELATIVE_PATH) === 0 : true)); // Subfolder installs need this additional check + var internalLink = utils.isInternalURI(this, window.location, RELATIVE_PATH); if ($(this).attr('data-ajaxify') === 'false') { if (!internalLink) { diff --git a/public/src/utils.js b/public/src/utils.js index 5d44bcd4fa..190d0ab3c6 100644 --- a/public/src/utils.js +++ b/public/src/utils.js @@ -431,6 +431,14 @@ } return utils.props(obj[prop], newProps, value); + }, + + isInternalURI: function(targetLocation, referenceLocation, relative_path) { + return targetLocation.host === '' || // Relative paths are always internal links + ( + targetLocation.host === referenceLocation.host && targetLocation.protocol === referenceLocation.protocol && // Otherwise need to check if protocol and host match + (relative_path.length > 0 ? targetLocation.pathname.indexOf(relative_path) === 0 : true) // Subfolder installs need this additional check + ); } }; diff --git a/src/controllers/authentication.js b/src/controllers/authentication.js index 98dc6c1fe4..0f74827be2 100644 --- a/src/controllers/authentication.js +++ b/src/controllers/authentication.js @@ -6,6 +6,7 @@ var passport = require('passport'); var nconf = require('nconf'); var validator = require('validator'); var _ = require('underscore'); +var url = require('url'); var db = require('../database'); var meta = require('../meta'); @@ -168,7 +169,7 @@ authenticationController.registerComplete = function(req, res, next) { } else { res.redirect(nconf.get('relative_path') + '/'); } - } + }; async.parallel(callbacks, function(err) { if (err) { @@ -187,7 +188,7 @@ authenticationController.registerComplete = function(req, res, next) { }); }; -authenticationController.registerAbort = function(req, res, next) { +authenticationController.registerAbort = function(req, res) { // End the session and redirect to home req.session.destroy(function() { res.redirect(nconf.get('relative_path') + '/'); @@ -197,7 +198,11 @@ authenticationController.registerAbort = function(req, res, next) { authenticationController.login = function(req, res, next) { // Handle returnTo data if (req.body.hasOwnProperty('returnTo') && !req.session.returnTo) { - req.session.returnTo = req.body.returnTo; + // As req.body is data obtained via userland, it is untrusted, restrict to internal links only + var parsed = url.parse(req.body.returnTo); + var isInternal = utils.isInternalURI(url.parse(req.body.returnTo), nconf.get('url_parsed'), nconf.get('relative_path')); + + req.session.returnTo = isInternal ? req.body.returnTo : nconf.get('url'); } if (plugins.hasListeners('action:auth.overrideLogin')) { From ebb50160cb9234a7c7b714dccbe0acf4462a2a64 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Mon, 22 Aug 2016 18:16:09 -0400 Subject: [PATCH 02/86] wrap saving of url_parsed in conditional, since url isn't set during setup --- app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 74beb77d6e..7fdee397f9 100644 --- a/app.js +++ b/app.js @@ -104,7 +104,9 @@ function loadConfig() { nconf.set('core_templates_path', path.join(__dirname, 'src/views')); nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-persona/templates')); - nconf.set('url_parsed', url.parse(nconf.get('url'))); + if (nconf.get('url')) { + nconf.set('url_parsed', url.parse(nconf.get('url'))); + } } From 862fd1a94a7aac4254f9f846ad2c380c662ce715 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Mon, 22 Aug 2016 17:16:52 -0500 Subject: [PATCH 03/86] ignore/watch categories recursively --- public/language/en_GB/category.json | 4 +-- src/socket.io/categories.js | 55 ++++++++++++++++++++++------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/public/language/en_GB/category.json b/public/language/en_GB/category.json index d4da76f356..d581c8277d 100644 --- a/public/language/en_GB/category.json +++ b/public/language/en_GB/category.json @@ -18,8 +18,8 @@ "watching.description": "Show topics in unread", "ignoring.description": "Do not show topics in unread", - "watch.message": "You are now watching updates from this category", - "ignore.message": "You are now ignoring updates from this category", + "watch.message": "You are now watching updates from this category and all subcategories", + "ignore.message": "You are now ignoring updates from this category and all subcategories", "watched-categories": "Watched categories" } diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index 545938d718..1c13adebea 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -1,6 +1,6 @@ 'use strict'; -var async = require('async'); +var async = require('async'); var db = require('../database'); var categories = require('../categories'); var privileges = require('../privileges'); @@ -171,23 +171,52 @@ SocketCategories.getMoveCategories = function(socket, data, callback) { }; SocketCategories.watch = function(socket, cid, callback) { - user.watchCategory(socket.uid, cid, function(err) { - if (err) { - return callback(err); - } - topics.pushUnreadCount(socket.uid, callback); - }); + ignoreOrWatch(user.watchCategory, socket, cid, callback); }; SocketCategories.ignore = function(socket, cid, callback) { - user.ignoreCategory(socket.uid, cid, function(err) { - if (err) { - return callback(err); - } - topics.pushUnreadCount(socket.uid, callback); - }); + ignoreOrWatch(user.ignoreCategory, socket, cid, callback); }; +function ignoreOrWatch(fn, socket, cid, callback) { + async.waterfall([ + function(next) { + db.getSortedSetRange('categories:cid', 0, -1, next); + }, + function(cids, next) { + categories.getCategoriesFields(cids, ['cid', 'parentCid'], next); + }, + function(categoryData, next) { + categoryData.forEach(function(c) { + c.cid = parseInt(c.cid, 10); + c.parentCid = parseInt(c.parentCid, 10); + }); + + var cids = [parseInt(cid, 10)]; + + // filter to subcategories of cid + + var any = true; + while (any) { + any = false; + categoryData.forEach(function(c) { + if (cids.indexOf(c.cid) === -1 && cids.indexOf(c.parentCid) !== -1) { + cids.push(c.cid); + any = true; + } + }); + } + + async.each(cids, function(cid, next) { + fn(socket.uid, cid, next); + }, next); + }, + function(next) { + topics.pushUnreadCount(socket.uid, next); + } + ], callback); +} + SocketCategories.isModerator = function(socket, cid, callback) { user.isModerator(socket.uid, cid, callback); }; From 61efabf4ec1f0ba024e4c03dd751164c42400380 Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Tue, 23 Aug 2016 09:03:30 -0400 Subject: [PATCH 04/86] Latest translations and fallbacks --- public/language/it/email.json | 4 +- public/language/it/error.json | 66 +++++++++++++-------------- public/language/it/global.json | 22 ++++----- public/language/it/groups.json | 2 +- public/language/it/login.json | 4 +- public/language/it/modules.json | 30 ++++++------ public/language/it/notifications.json | 22 ++++----- public/language/it/pages.json | 18 ++++---- public/language/it/recent.json | 2 +- public/language/it/register.json | 8 ++-- public/language/it/search.json | 4 +- public/language/it/topic.json | 32 ++++++------- public/language/it/unread.json | 6 +-- public/language/it/uploads.json | 8 ++-- public/language/it/user.json | 46 +++++++++---------- public/language/it/users.json | 2 +- 16 files changed, 138 insertions(+), 138 deletions(-) diff --git a/public/language/it/email.json b/public/language/it/email.json index ef28eac56e..8eddb0539d 100644 --- a/public/language/it/email.json +++ b/public/language/it/email.json @@ -17,7 +17,7 @@ "reset.notify.text1": "Ti informiamo che in data %1, la password è stata cambiata con successo.", "reset.notify.text2": "Se non hai autorizzato questo, per favore notifica immediatamente un amministratore.", "digest.notifications": "Hai una notifica non letta da %1:", - "digest.latest_topics": "Ultimi argomenti su %1", + "digest.latest_topics": "Ultime discussioni su %1", "digest.cta": "Clicca qui per visitare %1", "digest.unsub.info": "Questo sommario ti è stato inviato perché lo hai sottoscritto nelle tue impostazioni.", "digest.no_topics": "Non ci sono state discussioni attive nell'ultimo %1", @@ -28,7 +28,7 @@ "notif.chat.subject": "Nuovo messaggio in chat da %1", "notif.chat.cta": "Clicca qui per continuare la conversazione", "notif.chat.unsub.info": "Questa notifica di chat ti è stata inviata perché l'hai scelta nelle impostazioni.", - "notif.post.cta": "Clicca qui per leggere il topic completo.", + "notif.post.cta": "Clicca qui per leggere la discussione completa", "notif.post.unsub.info": "Questo post ti è stato notificato in base alle tue impostazioni di sottoscrizione.", "test.text1": "Questa è una email di test per verificare che il servizio di invio email è configurato correttamente sul tuo NodeBB.", "unsub.cta": "Clicca qui per modificare queste impostazioni", diff --git a/public/language/it/error.json b/public/language/it/error.json index 23b0710512..8fd8822deb 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -30,10 +30,10 @@ "password-too-long": "Password troppo lunga", "user-banned": "Utente bannato", "user-too-new": "Devi attendere %1 secondi prima di creare il tuo primo post", - "blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.", + "blacklisted-ip": "Purtroppo il tuo indirizzo IP è stato bannato da questa community. Se credi che ci sia stato un errore contatta un amministratore.", "ban-expiry-missing": "Please provide an end date for this ban", "no-category": "La Categoria non esiste", - "no-topic": "Il Topic non esiste", + "no-topic": "La discussione non esiste", "no-post": "Il Post non esiste", "no-group": "Il Gruppo non esiste", "no-user": "L'User non esiste", @@ -42,21 +42,21 @@ "category-disabled": "Categoria disabilitata", "topic-locked": "Discussione Bloccata", "post-edit-duration-expired": "Ti è consentito modificare un post per %1 secondi dopo averlo inviato", - "post-edit-duration-expired-minutes": "You are only allowed to edit posts for %1 minute(s) after posting", - "post-edit-duration-expired-minutes-seconds": "You are only allowed to edit posts for %1 minute(s) %2 second(s) after posting", - "post-edit-duration-expired-hours": "You are only allowed to edit posts for %1 hour(s) after posting", - "post-edit-duration-expired-hours-minutes": "You are only allowed to edit posts for %1 hour(s) %2 minute(s) after posting", - "post-edit-duration-expired-days": "You are only allowed to edit posts for %1 day(s) after posting", - "post-edit-duration-expired-days-hours": "You are only allowed to edit posts for %1 day(s) %2 hour(s) after posting", - "post-delete-duration-expired": "You are only allowed to delete posts for %1 second(s) after posting", - "post-delete-duration-expired-minutes": "You are only allowed to delete posts for %1 minute(s) after posting", - "post-delete-duration-expired-minutes-seconds": "You are only allowed to delete posts for %1 minute(s) %2 second(s) after posting", - "post-delete-duration-expired-hours": "You are only allowed to delete posts for %1 hour(s) after posting", - "post-delete-duration-expired-hours-minutes": "You are only allowed to delete posts for %1 hour(s) %2 minute(s) after posting", - "post-delete-duration-expired-days": "You are only allowed to delete posts for %1 day(s) after posting", - "post-delete-duration-expired-days-hours": "You are only allowed to delete posts for %1 day(s) %2 hour(s) after posting", - "cant-delete-topic-has-reply": "You can't delete your topic after it has a reply", - "cant-delete-topic-has-replies": "You can't delete your topic after it has %1 replies", + "post-edit-duration-expired-minutes": "Ti è permesso modificare i post solo per %1 minuto(i) dopo averli inviati", + "post-edit-duration-expired-minutes-seconds": "Ti è permesso modificare i post solo per %1 minuto(i) %secondo(i) dopo averli inviati", + "post-edit-duration-expired-hours": "Ti è permesso modificare i post solo per %1 ora(e) dopo averli inviati", + "post-edit-duration-expired-hours-minutes": "Ti è permesso modificare i post solo per %1 ora(e) %2 minuto(i) dopo averli inviati", + "post-edit-duration-expired-days": "Ti è permesso modificare i post solo per %1 giorno(i) dopo averli inviati", + "post-edit-duration-expired-days-hours": "Ti è permesso modificare i post solo per %1 giorno(i) %2 ora(e) dopo averli inviati", + "post-delete-duration-expired": "Ti è permesso eliminare i post solo per %1 secondo(i) dopo averli inviati", + "post-delete-duration-expired-minutes": "Ti è permesso eliminare i post solo per %1 minuto(i) dopo averli inviati", + "post-delete-duration-expired-minutes-seconds": "Ti è permesso eliminare i post solo per %1 minuto(i) %secondo(i) dopo averli inviati", + "post-delete-duration-expired-hours": "Ti è permesso eliminare i post solo per %1 ora(e) dopo averli inviati", + "post-delete-duration-expired-hours-minutes": "Ti è permesso eliminare i post solo per %1 giorno(i) %2 ora(e) dopo averli inviati", + "post-delete-duration-expired-days": "Ti è permesso eliminare i post solo per %1 giorno(i) dopo averli inviati", + "post-delete-duration-expired-days-hours": "Ti è permesso eliminare i post solo per %1 giorno(i) %2 ora(e) dopo averli inviati", + "cant-delete-topic-has-reply": "Non puoi eliminare la tua discussione se ha una risposta", + "cant-delete-topic-has-replies": "Non puoi eliminare la tua discussione se ha %1 risposte", "content-too-short": "Inserisci un testo più lungo. Il messaggio deve contenere almeno %1 caratteri.", "content-too-long": "Inserisci un post più breve. I post non possono essere più lunghi di %1 caratteri.", "title-too-short": "Inserisci un titolo più lungo. I titoli devono contenere almeno %1 caratteri.", @@ -65,25 +65,25 @@ "too-many-posts-newbie": "Come nuovo utente puoi postare solamente una volta ogni %1 secondi finché non hai raggiunto un livello di reputazione %2 - per favore attendi prima di scrivere ancora", "tag-too-short": "Inserisci un tag più lungo. I tag devono contenere almeno %1 caratteri.", "tag-too-long": "Per favore inserisci un tag più corto. I tags non dovrebbero essere più lunghi di %1 caratteri", - "not-enough-tags": "Tag non sufficienti. Gli argomenti devono avere almeno %1 Tag", - "too-many-tags": "Troppi Tag. Gli argomenti non possono avere più di %1 Tag", + "not-enough-tags": "Tag non sufficienti. Le discussioni devono avere almeno %1 Tag", + "too-many-tags": "Troppi Tag. Le discussioni non possono avere più di %1 Tag", "still-uploading": "Per favore attendere il completamento degli uploads.", "file-too-big": "La dimensione massima consentita è di %1 kB - si prega di caricare un file più piccolo", - "guest-upload-disabled": "Guest uploading has been disabled", + "guest-upload-disabled": "Il caricamento da ospite è stato disattivato", "already-favourited": "You have already bookmarked this post", "already-unfavourited": "You have already unbookmarked this post", "cant-ban-other-admins": "Non puoi bannare altri amministratori!", "cant-remove-last-admin": "Sei l'unico Amministratore. Aggiungi un altro amministratore prima di rimuovere il tuo ruolo", - "cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.", + "cant-delete-admin": "Togli i privilegi amministrativi da questo account prima di provare ad eliminarlo.", "invalid-image-type": "Tipo dell'immagine non valido. I tipi permessi sono: %1", "invalid-image-extension": "Estensione immagine non valida", "invalid-file-type": "Tipo di file non valido. I formati consentiti sono: %1", "group-name-too-short": "Nome del Gruppo troppo corto", - "group-name-too-long": "Group name too long", + "group-name-too-long": "Il nome del gruppo è troppo lungo", "group-already-exists": "Il Gruppo esiste già", "group-name-change-not-allowed": "Il cambio di nome al Gruppo non è consentito", - "group-already-member": "Already part of this group", - "group-not-member": "Not a member of this group", + "group-already-member": "Fa già parte di questo gruppo", + "group-not-member": "Non è membro di questo gruppo", "group-needs-owner": "Questo gruppo richiede almeno un proprietario.", "group-already-invited": "Questo utente è già stato invitato", "group-already-requested": "La tua richiesta di partecipazione è già stata inviata", @@ -99,14 +99,14 @@ "about-me-too-long": "Spiacenti, il testo non può essere più lungo di %1 caratteri.", "cant-chat-with-yourself": "Non puoi chattare con te stesso!", "chat-restricted": "Questo utente ha ristretto i suoi messaggi in chat alle persone che segue. Per poter chattare con te ti deve prima seguire.", - "chat-disabled": "Chat system disabled", + "chat-disabled": "Il sistema di chat è stato disabilitato", "too-many-messages": "Hai inviato troppi messaggi, aspetta un attimo.", "invalid-chat-message": "Messaggio chat non valido", "chat-message-too-long": "Il messaggio chat è troppo lungo", - "cant-edit-chat-message": "You are not allowed to edit this message", - "cant-remove-last-user": "You can't remove the last user", - "cant-delete-chat-message": "You are not allowed to delete this message", - "already-voting-for-this-post": "You have already voted for this post.", + "cant-edit-chat-message": "Non ti è permesso di modificare questo messaggio", + "cant-remove-last-user": "Non puoi rimuovere l'ultimo utente", + "cant-delete-chat-message": "Non ti è permesso di eliminare questo messaggio", + "already-voting-for-this-post": "Hai già votato per questo post", "reputation-system-disabled": "Il sistema di reputazione è disabilitato.", "downvoting-disabled": "Il Downvoting è disabilitato", "not-enough-reputation-to-downvote": "Non hai i privilegi per votare negativamente questo post", @@ -119,9 +119,9 @@ "wrong-login-type-username": "Per favore usa il tuo nome utente per accedere", "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).", "no-session-found": "No login session found!", - "not-in-room": "User not in room", - "no-users-in-room": "No users in this room", - "cant-kick-self": "You can't kick yourself from the group", - "no-users-selected": "No user(s) selected", + "not-in-room": "L'utente non è in questa stanza", + "no-users-in-room": "Nessun utente in questa stanza", + "cant-kick-self": "Non puoi espellerti dal gruppo", + "no-users-selected": "Nessun utente selezionato", "invalid-home-page-route": "Invalid home page route" } \ No newline at end of file diff --git a/public/language/it/global.json b/public/language/it/global.json index b20daeef18..ccfff6ad6e 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -49,7 +49,7 @@ "users": "Utenti", "topics": "Discussioni", "posts": "Post", - "best": "Best", + "best": "Migliore", "upvoters": "Upvoters", "upvoted": "Upvoted", "downvoters": "Downvoters", @@ -61,15 +61,15 @@ "posted_ago_by_guest": "scritto %1 di Ospite", "posted_ago_by": "scritto %1 di %2", "posted_ago": "postato %1", - "posted_in": "posted in %1", - "posted_in_by": "posted in %1 by %2", + "posted_in": "postato in %1", + "posted_in_by": "postato in %1 da %2", "posted_in_ago": "postato in %1 %2", "posted_in_ago_by": "postato in %1 %2 da %3", "user_posted_ago": "%1 ha postato %2", "guest_posted_ago": "Ospite ha scritto %1", - "last_edited_by": "last edited by %1", + "last_edited_by": "ultima modifica di %1", "norecentposts": "Nessun Post Recente", - "norecenttopics": "Nessun Argomento Recente", + "norecenttopics": "Nessuna Discussione Recente", "recentposts": "Post Recenti", "recentips": "Indirizzi IP Recentemente Loggati", "away": "Non disponibile", @@ -87,11 +87,11 @@ "unfollow": "Non seguire", "delete_all": "Elimina Tutto", "map": "Mappa", - "sessions": "Login Sessions", - "ip_address": "IP Address", - "enter_page_number": "Enter page number", - "upload_file": "Upload file", - "upload": "Upload", + "sessions": "Sessioni di Login", + "ip_address": "Indirizzo IP", + "enter_page_number": "Inserisci il numero della pagina", + "upload_file": "Carica file", + "upload": "Carica", "allowed-file-types": "Allowed file types are %1", - "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?" + "unsaved-changes": "Hai delle modifiche non salvate. Sei sicuro che vuoi lasciare la pagina?" } \ No newline at end of file diff --git a/public/language/it/groups.json b/public/language/it/groups.json index ca2d7f898a..863b4be850 100644 --- a/public/language/it/groups.json +++ b/public/language/it/groups.json @@ -50,5 +50,5 @@ "membership.leave-group": "Lascia il Gruppo", "membership.reject": "Rifiuta", "new-group.group_name": "Nome Gruppo:", - "upload-group-cover": "Upload group cover" + "upload-group-cover": "Carica copertina del gruppo" } \ No newline at end of file diff --git a/public/language/it/login.json b/public/language/it/login.json index 3c875d4195..1561a8484b 100644 --- a/public/language/it/login.json +++ b/public/language/it/login.json @@ -5,8 +5,8 @@ "remember_me": "Ricordami?", "forgot_password": "Password dimenticata?", "alternative_logins": "Accessi Alternativi", - "failed_login_attempt": "Login Unsuccessful", + "failed_login_attempt": "Tentativo di accesso fallito", "login_successful": "Sei entrato con successo!", "dont_have_account": "Non hai un account?", - "logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity" + "logged-out-due-to-inactivity": "Sei stato disconnesso dal Pannello di Controllo Amministratore per inattività" } \ No newline at end of file diff --git a/public/language/it/modules.json b/public/language/it/modules.json index d18485ef7d..2eaf84aba9 100644 --- a/public/language/it/modules.json +++ b/public/language/it/modules.json @@ -6,9 +6,9 @@ "chat.user_typing": "%1 sta scrivendo...", "chat.user_has_messaged_you": "%1 ti ha scritto.", "chat.see_all": "Vedi tutte le chat", - "chat.mark_all_read": "Mark all chats read", + "chat.mark_all_read": "Segna tutti i messaggi come già letti", "chat.no-messages": "Si prega di selezionare un destinatario per vedere la cronologia dei messaggi", - "chat.no-users-in-room": "No users in this room", + "chat.no-users-in-room": "Nessun utente in questa stanza", "chat.recent-chats": "Chat Recenti", "chat.contacts": "Contatti", "chat.message-history": "Cronologia Messaggi", @@ -17,9 +17,9 @@ "chat.seven_days": "7 Giorni", "chat.thirty_days": "30 Giorni", "chat.three_months": "3 Mesi", - "chat.delete_message_confirm": "Are you sure you wish to delete this message?", + "chat.delete_message_confirm": "Sei sicuro di voler eliminare questo messaggio?", "chat.roomname": "Chat Room %1", - "chat.add-users-to-room": "Add users to room", + "chat.add-users-to-room": "Aggiungi utenti alla stanza", "composer.compose": "Componi", "composer.show_preview": "Visualizza Anteprima", "composer.hide_preview": "Nascondi Anteprima", @@ -28,20 +28,20 @@ "composer.discard": "Sei sicuro di voler scartare questo post?", "composer.submit_and_lock": "Invia e Blocca", "composer.toggle_dropdown": "Mostra/Nascondi menu a discesa", - "composer.uploading": "Uploading %1", - "composer.formatting.bold": "Bold", - "composer.formatting.italic": "Italic", - "composer.formatting.list": "List", + "composer.uploading": "Caricamento %1", + "composer.formatting.bold": "Grassetto", + "composer.formatting.italic": "Corsivo", + "composer.formatting.list": "Lista", "composer.formatting.strikethrough": "Strikethrough", - "composer.formatting.link": "Link", - "composer.formatting.picture": "Picture", - "composer.upload-picture": "Upload Image", - "composer.upload-file": "Upload File", + "composer.formatting.link": "Collegamento", + "composer.formatting.picture": "Immagine", + "composer.upload-picture": "Carica immagine", + "composer.upload-file": "Carica file", "composer.zen_mode": "Zen Mode", "bootbox.ok": "OK", "bootbox.cancel": "Annulla", "bootbox.confirm": "Conferma", - "cover.dragging_title": "Cover Photo Positioning", - "cover.dragging_message": "Drag the cover photo to the desired position and click \"Save\"", - "cover.saved": "Cover photo image and position saved" + "cover.dragging_title": "Posizionando la foto copertina", + "cover.dragging_message": "Trascina l'immagine di copertina nella posizione desiderata e clicca su \"Salva\"", + "cover.saved": "Immagine di copertina e posizione salvati" } \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index bd224b2cea..c7725575b5 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -5,29 +5,29 @@ "mark_all_read": "Segna tutte le notifiche come già lette", "back_to_home": "Indietro a %1", "outgoing_link": "Link in uscita", - "outgoing_link_message": "You are now leaving %1", + "outgoing_link_message": "Stai lasciando %1", "continue_to": "Continua a %1", "return_to": "Ritorna a %1", "new_notification": "Nuova Notifica", "you_have_unread_notifications": "Hai notifiche non lette.", "new_message_from": "Nuovo messaggio da %1", "upvoted_your_post_in": "%1 ha votato positivamente il tuo post in %2.", - "upvoted_your_post_in_dual": "%1 and %2 have upvoted your post in %3.", - "upvoted_your_post_in_multiple": "%1 and %2 others have upvoted your post in %3.", - "moved_your_post": "%1 has moved your post to %2", - "moved_your_topic": "%1 has moved %2", + "upvoted_your_post_in_dual": "%1 e %2 hanno apprezzato il tuo post in %3.", + "upvoted_your_post_in_multiple": "%1 ed altri %2 hanno apprezzato il tuo post in %3.", + "moved_your_post": "%1 ha spostato il tuo post su %2", + "moved_your_topic": "%1 è stato spostato %2", "user_flagged_post_in": "%1 ha segnalato un post in %2", "user_flagged_post_in_dual": "%1 and %2 flagged a post in %3", "user_flagged_post_in_multiple": "%1 and %2 others flagged a post in %3", "user_posted_to": "%1 ha postato una risposta a: %2", - "user_posted_to_dual": "%1 and %2 have posted replies to: %3", - "user_posted_to_multiple": "%1 and %2 others have posted replies to: %3", - "user_posted_topic": "%1 ha postato un nuovo Topic: %2", + "user_posted_to_dual": "%1 e %2 hanno postato una risposta su: %3", + "user_posted_to_multiple": "%1 ed altri %2 hanno postato una risposta su: %3", + "user_posted_topic": "%1 ha postato una nuova discussione: %2", "user_started_following_you": "%1 ha iniziato a seguirti.", - "user_started_following_you_dual": "%1 and %2 started following you.", - "user_started_following_you_multiple": "%1 and %2 others started following you.", + "user_started_following_you_dual": "%1 e %2 hanno iniziato a seguirti.", + "user_started_following_you_multiple": "%1 ed altri %2 hanno iniziato a seguirti.", "new_register": "%1 ha inviato una richiesta di registrazione.", - "new_register_multiple": "There are %1 registration requests awaiting review.", + "new_register_multiple": "Ci sono %1 richieste di registrazione che attendono di essere esaminate.", "email-confirmed": "Email Confermata", "email-confirmed-message": "Grazie per aver validato la tua email. Il tuo account è ora completamente attivato.", "email-confirm-error-message": "C'è stato un problema nella validazione del tuo indirizzo email. Potrebbe essere il codice non valido o scaduto.", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index da115161ee..e509b23ef8 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -11,7 +11,7 @@ "users/latest": "Ultimi Utenti", "users/sort-posts": "Utenti maggiori contributori", "users/sort-reputation": "Utenti con la reputazione più alta", - "users/banned": "Banned Users", + "users/banned": "Utenti Bannati", "users/most-flags": "Most flagged users", "users/search": "Ricerca Utenti", "notifications": "Notifiche", @@ -26,23 +26,23 @@ "chats": "Chat", "chat": "In chat con %1", "account/edit": "Modificando \"%1\"", - "account/edit/password": "Editing password of \"%1\"", - "account/edit/username": "Editing username of \"%1\"", - "account/edit/email": "Editing email of \"%1\"", + "account/edit/password": "Modificando la password di \"%1\"", + "account/edit/username": "Modificando il nome utente di \"%1\"", + "account/edit/email": "Modificando l'email di \"%1\"", "account/info": "Account Info", "account/following": "Persone seguite da %1", "account/followers": "Persone che seguono %1", "account/posts": "Post creati da %1", "account/topics": "Discussioni create da %1", "account/groups": "Gruppi di %1", - "account/favourites": "%1's Bookmarked Posts", + "account/favourites": "Post Preferiti da %1", "account/settings": "Impostazioni Utente", "account/watched": "Discussioni osservate da %1", - "account/upvoted": "Posts upvoted by %1", + "account/upvoted": "Post apprezzati da %1", "account/downvoted": "Posts downvoted by %1", - "account/best": "Best posts made by %1", - "confirm": "Email Confirmed", + "account/best": "I migliori post di %1", + "confirm": "Email Confermata", "maintenance.text": "%1 è attualmente in manutenzione. Per favore ritorna più tardi.", "maintenance.messageIntro": "Inoltre, l'amministratore ha lasciato questo messaggio:", - "throttled.text": "%1 is currently unavailable due to excessive load. Please come back another time." + "throttled.text": "%1 non è al momento disponibile a causa di un carico eccessivo. Per favore ritorna più tardi." } \ No newline at end of file diff --git a/public/language/it/recent.json b/public/language/it/recent.json index c61e51f150..a32a7b5d3c 100644 --- a/public/language/it/recent.json +++ b/public/language/it/recent.json @@ -6,7 +6,7 @@ "year": "Anno", "alltime": "Sempre", "no_recent_topics": "Non ci sono discussioni recenti.", - "no_popular_topics": "Non ci sono argomenti popolari.", + "no_popular_topics": "Non ci sono discussioni popolari.", "there-is-a-new-topic": "C'è un nuovo topic.", "there-is-a-new-topic-and-a-new-post": "C'è un nuovo topic e un nuovo post.", "there-is-a-new-topic-and-new-posts": "C'è una nuova discussione e %1 nuovi post.", diff --git a/public/language/it/register.json b/public/language/it/register.json index 58a04bf477..e3afd5aed2 100644 --- a/public/language/it/register.json +++ b/public/language/it/register.json @@ -1,6 +1,6 @@ { "register": "Registrazione", - "cancel_registration": "Cancel Registration", + "cancel_registration": "Cancella Registrazione", "help.email": "Come opzione predefinita, il tuo indirizzo email non verrà reso pubblico.", "help.username_restrictions": "Un nome utente unico, di almeno %1 caratteri e al massimo di %2. Gli altri utenti ti possono menzionare usando @username.", "help.minimum_password_length": "La lunghezza della password deve essere di almeno %1 caratteri.", @@ -16,8 +16,8 @@ "alternative_registration": "Altri metodi di registrazione", "terms_of_use": "Termini di Utilizzo", "agree_to_terms_of_use": "Accetto i Termini di Utilizzo", - "terms_of_use_error": "You must agree to the Terms of Use", + "terms_of_use_error": "Devi accettare i Termini d'Utilizzo", "registration-added-to-queue": "La tua registrazione è stata aggiunta alla coda di moderazione. Riceverai una mail quando verrà accettata da un amministratore.", - "interstitial.intro": "We require some additional information before we can create your account.", - "interstitial.errors-found": "We could not complete your registration:" + "interstitial.intro": "Abbiamo bisogno di qualche informazione in più prima di poter creare il tuo account.", + "interstitial.errors-found": "Non abbiamo potuto completare la tua registrazione:" } \ No newline at end of file diff --git a/public/language/it/search.json b/public/language/it/search.json index a3e73da887..9234c43389 100644 --- a/public/language/it/search.json +++ b/public/language/it/search.json @@ -24,10 +24,10 @@ "one-year": "Un anno", "sort-by": "Ordina per", "last-reply-time": "Ora dell'ultima risposta", - "topic-title": "Titolo argomento", + "topic-title": "Titolo discussione", "number-of-replies": "Numero di risposte", "number-of-views": "Numero di visite", - "topic-start-date": "Data inizio argomento", + "topic-start-date": "Discussione iniziata", "username": "Nome utente", "category": "Categoria", "descending": "In ordine decrescente", diff --git a/public/language/it/topic.json b/public/language/it/topic.json index 1834ff19cd..5367678fa8 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -26,30 +26,30 @@ "tools": "Strumenti", "flag": "Segnala", "locked": "Bloccato", - "pinned": "Pinned", - "moved": "Moved", - "bookmark_instructions": "Click here to return to the last read post in this thread.", + "pinned": "Appeso", + "moved": "Spostato", + "bookmark_instructions": "Clicca qui per tornare all'ultimo post letto in questa discussione.", "flag_title": "Segnala questo post per la moderazione", "flag_success": "Questo post è stato contrassegnato per la moderazione.", "deleted_message": "Questa discussione è stata cancellata. Solo gli utenti con diritti di gestione possono vederla.", "following_topic.message": "Da ora riceverai notifiche quando qualcuno posterà in questa discussione.", - "not_following_topic.message": "You will see this topic in the unread topics list, but you will not receive notifications when somebody posts to this topic.", + "not_following_topic.message": "Vedrai questa discussione nella lista delle discussioni non lette, ma non riceverai notifiche quando qualcuno risponde a questa discussione.", "ignoring_topic.message": "You will no longer see this topic in the unread topics list. You will be notified when you are mentioned or your post is up voted.", "login_to_subscribe": "Si prega di accedere o registrarsi per potersi iscrivere a questa discussione.", "markAsUnreadForAll.success": "Discussione segnata come non letta per tutti.", "mark_unread": "Segna come non letto", - "mark_unread.success": "Topic marked as unread.", + "mark_unread.success": "Discussione è stata marcata come non letta.", "watch": "Osserva", "unwatch": "Non osservare più", "watch.title": "Ricevi notifiche di nuove risposte in questa discussione", "unwatch.title": "Smetti di osservare questa discussione", "share_this_post": "Condividi questo Post", - "watching": "Watching", - "not-watching": "Not Watching", - "ignoring": "Ignoring", + "watching": "Seguito", + "not-watching": "Non Seguito", + "ignoring": "Ignorato", "watching.description": "Notify me of new replies.
Show topic in unread.", - "not-watching.description": "Do not notify me of new replies.
Show topic in unread if category is not ignored.", - "ignoring.description": "Do not notify me of new replies.
Do not show topic in unread.", + "not-watching.description": "Non notificarmi sulle nuove risposte.
Mostra la discussione fra le non lette se la categoria non è ignorata.", + "ignoring.description": "Non notificarmi sulle nuove risposte.
Non mostrare la discussione fra le non lette.", "thread_tools.title": "Strumenti per la Discussione", "thread_tools.markAsUnreadForAll": "Segna come non letto", "thread_tools.pin": "Fissa Discussione", @@ -61,7 +61,7 @@ "thread_tools.fork": "Dividi Discussione", "thread_tools.delete": "Elimina Discussione", "thread_tools.delete-posts": "Cancella post", - "thread_tools.delete_confirm": "Sei sicuro di voler cancellare questa discussione?", + "thread_tools.delete_confirm": "Sei sicuro di voler eliminare questa discussione?", "thread_tools.restore": "Ripristina Discussione", "thread_tools.restore_confirm": "Sei sicuro di voler ripristinare questa discussione?", "thread_tools.purge": "Svuota Discussione", @@ -86,7 +86,7 @@ "topic_will_be_moved_to": "Questa discussione verrà spostata nella categoria", "fork_topic_instruction": "Clicca sui post che vuoi dividere", "fork_no_pids": "Nessun post selezionato!", - "fork_pid_count": "%1 post(s) selected", + "fork_pid_count": "%1 post selezionati", "fork_success": "Topic Diviso con successo ! Clicca qui per andare al Topic Diviso.", "delete_posts_instruction": "Clicca sui post che vuoi cancellare/eliminare", "composer.title_placeholder": "Inserisci qui il titolo della discussione...", @@ -94,7 +94,7 @@ "composer.discard": "Annulla", "composer.submit": "Invia", "composer.replying_to": "Rispondendo a %1", - "composer.new_topic": "Nuovo Argomento", + "composer.new_topic": "Nuova Discussione", "composer.uploading": "caricamento...", "composer.thumb_url_label": "Incolla l'URL di una immagine per la discussione", "composer.thumb_title": "Aggiungi un'immagine a questa discussione", @@ -111,10 +111,10 @@ "newest_to_oldest": "Da Nuovi a Vecchi", "most_votes": "Più votati", "most_posts": "Ulteriori post", - "stale.title": "Preferisci creare un nuovo topic?", + "stale.title": "Preferisci creare una nuova discussione?", "stale.warning": "Il topic al quale stai rispondendo è abbastanza vecchio. Vorresti piuttosto creare un nuovo topic in riferimento a questo nella tua risposta?", - "stale.create": "Crea un topic nuovo", - "stale.reply_anyway": "Rispondi a questo topic comunque", + "stale.create": "Crea una nuova discussione", + "stale.reply_anyway": "Rispondi comunque a questa discussione", "link_back": "Re: [%1](%2)", "spam": "Spam", "offensive": "Offensivo", diff --git a/public/language/it/unread.json b/public/language/it/unread.json index 94c4a315a3..f03f876183 100644 --- a/public/language/it/unread.json +++ b/public/language/it/unread.json @@ -7,7 +7,7 @@ "all": "Tutti", "all_categories": "Tutte le categorie", "topics_marked_as_read.success": "Discussione marcata come letta!", - "all-topics": "All Topics", - "new-topics": "New Topics", - "watched-topics": "Watched Topics" + "all-topics": "Tutte le Discussioni", + "new-topics": "Nuova Discussione", + "watched-topics": "Discussioni seguite" } \ No newline at end of file diff --git a/public/language/it/uploads.json b/public/language/it/uploads.json index 1622cb5693..fcc163f5c7 100644 --- a/public/language/it/uploads.json +++ b/public/language/it/uploads.json @@ -1,6 +1,6 @@ { - "uploading-file": "Uploading the file...", - "select-file-to-upload": "Select a file to upload!", - "upload-success": "File uploaded successfully!", - "maximum-file-size": "Maximum %1 kb" + "uploading-file": "Sto caricando il file...", + "select-file-to-upload": "Seleziona un file da caricare!", + "upload-success": "File caricato con successo!", + "maximum-file-size": "Massimo %1 kb" } \ No newline at end of file diff --git a/public/language/it/user.json b/public/language/it/user.json index 1c864d034b..939f0e0923 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -6,7 +6,7 @@ "postcount": "Numero post", "email": "Email", "confirm_email": "Conferma Email", - "account_info": "Account Info", + "account_info": "Informazioni dell'account", "ban_account": "BAN dell'account", "ban_account_confirm": "Sei sicuro di voler bannare questo utente?", "unban_account": "Togli il BAN", @@ -23,7 +23,7 @@ "profile": "Profilo", "profile_views": "Visite al profilo", "reputation": "Reputazione", - "favourites": "Bookmarks", + "favourites": "Preferiti", "watched": "Osservati", "followers": "Da chi è seguito", "following": "Chi segue", @@ -31,17 +31,17 @@ "signature": "Firma", "birthday": "Data di nascita", "chat": "Chat", - "chat_with": "Chat with %1", + "chat_with": "Chatta con %1", "follow": "Segui", "unfollow": "Smetti di seguire", "more": "Altro", "profile_update_success": "Profilo aggiornato correttamente!", "change_picture": "Cambia Foto", - "change_username": "Change Username", - "change_email": "Change Email", + "change_username": "Modifica il nome utente", + "change_email": "Modifica Email", "edit": "Modifica", - "edit-profile": "Edit Profile", - "default_picture": "Default Icon", + "edit-profile": "Modifica Profilo", + "default_picture": "Icona di default", "uploaded_picture": "Foto caricata", "upload_new_picture": "Carica una nuova foto", "upload_new_picture_from_url": "Carica nuova immagine da URL", @@ -57,17 +57,17 @@ "password": "Password", "username_taken_workaround": "Il nome utente che hai richiesto era già stato utilizzato, quindi lo abbiamo modificato leggermente. Ora il tuo è %1", "password_same_as_username": "La tua password è uguale al tuo username, per piacere scegli un'altra password", - "password_same_as_email": "Your password is the same as your email, please select another password.", + "password_same_as_email": "La tua password sembra coincidere con la tua email, per favore fornisci un'altra password.", "upload_picture": "Carica foto", "upload_a_picture": "Carica una foto", "remove_uploaded_picture": "Elimina foto caricata", - "upload_cover_picture": "Upload cover picture", + "upload_cover_picture": "Carica immagine di copertina", "settings": "Impostazioni", "show_email": "Mostra la mia Email", "show_fullname": "Vedi il Mio Nome Completo", "restrict_chats": "Abilita messaggi in chat soltanto dagli utenti che seguo", "digest_label": "Iscriviti al Sommario", - "digest_description": "Abbonati agli aggiornamenti via email di questo forum (nuove notifiche e argomenti) secondo una pianificazione impostata", + "digest_description": "Abbonati agli aggiornamenti via email di questo forum (nuove notifiche e discussioni) secondo una pianificazione impostata", "digest_off": "Spento", "digest_daily": "Quotidiano", "digest_weekly": "Settimanale", @@ -82,7 +82,7 @@ "has_no_watched_topics": "Questo utente non sta osservando discussioni.", "has_no_upvoted_posts": "This user hasn't upvoted any posts yet.", "has_no_downvoted_posts": "This user hasn't downvoted any posts yet.", - "has_no_voted_posts": "This user has no voted posts", + "has_no_voted_posts": "Questo utente non ha post votati", "email_hidden": "Email Nascosta", "hidden": "nascosta", "paginate_description": "Non utilizzare lo scroll infinito per discussioni e messaggi", @@ -94,16 +94,16 @@ "enable_topic_searching": "Abilita la ricerca negli argomenti", "topic_search_help": "Se abilitata, la ricerca negli argomenti ignorerà il comportamento predefinito del browser per consentirti di cercare all'interno delle discussioni, anziché soltanto nel contenuto visibile a schermo", "delay_image_loading": "Delay Image Loading", - "image_load_delay_help": "If enabled, images in topics will not load until they are scrolled into view", - "scroll_to_my_post": "After posting a reply, show the new post", - "follow_topics_you_reply_to": "Watch topics that you reply to", - "follow_topics_you_create": "Watch topics you create", - "grouptitle": "Group Title", + "image_load_delay_help": "Se selezionato, le immagini nelle discussioni non saranno caricate finché non sono visibili nello schermo", + "scroll_to_my_post": "Dopo aver postato una risposta, mostra il nuovo post", + "follow_topics_you_reply_to": "Segui le discussioni a cui rispondi", + "follow_topics_you_create": "Segui le discussioni che crei", + "grouptitle": "Titolo del Gruppo", "no-group-title": "Nessun titolo al gruppo", "select-skin": "Seleziona uno Skin", - "select-homepage": "Select a Homepage", - "homepage": "Homepage", - "homepage_description": "Select a page to use as the forum homepage or 'None' to use the default homepage.", + "select-homepage": "Seleziona una Pagina Iniziale", + "homepage": "Pagina iniziale", + "homepage_description": "Seleziona una pagina da usare come pagina iniziale o \"Nessuna\" per usare quella di default.", "custom_route": "Custom Homepage Route", "custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\", or \"popular\")", "sso.title": "Servizi Single-Sign-On", @@ -111,8 +111,8 @@ "sso.not-associated": "Clicca qui per associare con", "info.latest-flags": "Latest Flags", "info.no-flags": "No Flagged Posts Found", - "info.ban-history": "Recent Ban History", - "info.no-ban-history": "This user has never been banned", - "info.banned-until": "Banned until %1", - "info.banned-permanently": "Banned permanently" + "info.ban-history": "Storico dei Ban recenti", + "info.no-ban-history": "Questo utente non è mai stato bannato", + "info.banned-until": "Bannato fino %1", + "info.banned-permanently": "Bannato permanentemente" } \ No newline at end of file diff --git a/public/language/it/users.json b/public/language/it/users.json index 3299cd0459..c88f786172 100644 --- a/public/language/it/users.json +++ b/public/language/it/users.json @@ -17,5 +17,5 @@ "unread_topics": "Discussioni non lette", "categories": "Categorie", "tags": "Tag", - "no-users-found": "No users found!" + "no-users-found": "Nessun utente trovato!" } \ No newline at end of file From 31751c9d8a877c447b8a0669ea288c511e1acec6 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 23 Aug 2016 18:47:07 +0300 Subject: [PATCH 05/86] closes #4960 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4102742aab..b37e83e63d 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.23", - "nodebb-theme-vanilla": "5.1.11", + "nodebb-theme-persona": "4.1.24", + "nodebb-theme-vanilla": "5.1.12", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From 5d5b74f3edcb6afd0b7430a9836d8c8dd0b7eae8 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 23 Aug 2016 19:59:25 +0300 Subject: [PATCH 06/86] if no sorting specificed let search plugins handle --- src/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/search.js b/src/search.js index 9b7306c7fa..b34704bf47 100644 --- a/src/search.js +++ b/src/search.js @@ -298,10 +298,10 @@ function filterByTimerange(posts, timeRange, timeFilter) { } function sortPosts(posts, data) { - if (!posts.length) { + if (!posts.length || !data.sortBy) { return; } - data.sortBy = data.sortBy || 'timestamp'; + data.sortDirection = data.sortDirection || 'desc'; var direction = data.sortDirection === 'desc' ? 1 : -1; From 34276dd638d5b883b416040f3ddced5dcf4067d9 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Tue, 23 Aug 2016 20:04:25 +0300 Subject: [PATCH 07/86] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b37e83e63d..1f452613a3 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.24", - "nodebb-theme-vanilla": "5.1.12", + "nodebb-theme-persona": "4.1.25", + "nodebb-theme-vanilla": "5.1.13", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From dbdbfc6d7537a2a1b0314c121b0e627e02565b2c Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Tue, 23 Aug 2016 15:10:46 -0400 Subject: [PATCH 08/86] allowing the port to be defined by a custom port environment variable, for certain hosting environments --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 7fdee397f9..c633af9932 100644 --- a/app.js +++ b/app.js @@ -124,7 +124,7 @@ function start() { nconf.set('secure', urlObject.protocol === 'https:'); nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); - nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567); + nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); nconf.set('upload_url', nconf.get('upload_path').replace(/^\/public/, '')); if (nconf.get('isPrimary') === 'true') { From 598935b3bfbca89fe176c7f2050325e6de6811d9 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Tue, 23 Aug 2016 19:35:50 -0500 Subject: [PATCH 09/86] Fix null object in search for users with no preferences set. localStorage.getItem returned null, and JSON.parse(null) is null. --- public/src/modules/search.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/src/modules/search.js b/public/src/modules/search.js index be6ac6a711..147683c550 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -75,7 +75,7 @@ define('search', ['navigator', 'translator'], function(nav, translator) { Search.getSearchPreferences = function() { try { - return JSON.parse(localStorage.getItem('search-preferences')); + return JSON.parse(localStorage.getItem('search-preferences') || '{}'); } catch(e) { return {}; } @@ -179,4 +179,4 @@ define('search', ['navigator', 'translator'], function(nav, translator) { }; return Search; -}); \ No newline at end of file +}); From 0fb74a2636823f9b2add0960bc4d757f5fdbe37c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 24 Aug 2016 12:14:28 +0300 Subject: [PATCH 10/86] pass err object along so errors dont get treated as 404 @julianlam --- src/controllers/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/index.js b/src/controllers/index.js index 47047924a4..975dca54ae 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -410,7 +410,7 @@ Controllers.handleURIErrors = function(err, req, res, next) { return; } else { - next(); + next(err); } }; From 84f88a6f15126250bdfcf666c20be28de4a0d45a Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 24 Aug 2016 14:47:01 +0300 Subject: [PATCH 11/86] refactor user search use pagination on results removed infinite scroll changed the term and section to use the query param as well pagination urls respect search --- public/src/client/account/followers.js | 34 +----- public/src/client/account/following.js | 6 +- public/src/client/search.js | 17 ++- public/src/client/users.js | 145 +++++++++---------------- public/src/modules/search.js | 15 +-- src/controllers/accounts/follow.js | 20 +++- src/controllers/accounts/helpers.js | 14 ++- src/controllers/search.js | 4 +- src/controllers/users.js | 79 +++++++++++--- src/routes/index.js | 27 ++--- src/socket.io/user.js | 45 -------- src/user/search.js | 11 +- 12 files changed, 181 insertions(+), 236 deletions(-) diff --git a/public/src/client/account/followers.js b/public/src/client/account/followers.js index 91d3f4daaa..8a2d15f0b0 100644 --- a/public/src/client/account/followers.js +++ b/public/src/client/account/followers.js @@ -1,43 +1,13 @@ 'use strict'; -/* globals define, socket, utils */ +/* globals define */ -define('forum/account/followers', ['forum/account/header', 'forum/infinitescroll'], function(header, infinitescroll) { +define('forum/account/followers', ['forum/account/header'], function(header) { var Followers = {}; Followers.init = function() { header.init(); - - infinitescroll.init(function(direction) { - Followers.loadMore(direction, 'account/followers', 'followers:' + ajaxify.data.uid); - }); }; - Followers.loadMore = function(direction, tpl, set) { - if (direction < 0) { - return; - } - - infinitescroll.loadMore('user.loadMore', { - set: set, - after: $('#users-container').attr('data-nextstart') - }, function(data, done) { - if (data.users && data.users.length) { - onUsersLoaded(tpl, data.users, done); - $('#users-container').attr('data-nextstart', data.nextStart); - } else { - done(); - } - }); - }; - - function onUsersLoaded(tpl, users, callback) { - app.parseAndTranslate(tpl, 'users', {users: users}, function(html) { - $('#users-container').append(html); - utils.addCommasToNumbers(html.find('.formatted-number')); - callback(); - }); - } - return Followers; }); diff --git a/public/src/client/account/following.js b/public/src/client/account/following.js index 8e421b1892..04230fb979 100644 --- a/public/src/client/account/following.js +++ b/public/src/client/account/following.js @@ -2,15 +2,11 @@ /* globals define */ -define('forum/account/following', ['forum/account/header', 'forum/infinitescroll', 'forum/account/followers'], function(header, infinitescroll, followers) { +define('forum/account/following', ['forum/account/header'], function(header) { var Following = {}; Following.init = function() { header.init(); - - infinitescroll.init(function(direction) { - followers.loadMore(direction, 'account/following', 'following:' + ajaxify.data.uid); - }); }; return Following; diff --git a/public/src/client/search.js b/public/src/client/search.js index bf07ad70b6..b9632513bc 100644 --- a/public/src/client/search.js +++ b/public/src/client/search.js @@ -22,15 +22,10 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco $('#advanced-search').off('submit').on('submit', function(e) { e.preventDefault(); - - var input = $('#search-input'); - - var searchData = getSearchData(); - searchData.term = input.val(); - - searchModule.query(searchData, function() { - input.val(''); + searchModule.query(getSearchData(), function() { + $('#search-input').val(''); }); + return false; }); handleSavePreferences(); @@ -43,7 +38,7 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco var searchData = { in: $('#search-in').val() }; - + searchData.term = $('#search-input').val(); if (searchData.in === 'posts' || searchData.in === 'titlesposts' || searchData.in === 'titles') { searchData.by = form.find('#posted-by-user').val(); searchData.categories = form.find('#posted-in-categories').val(); @@ -71,6 +66,10 @@ define('forum/search', ['search', 'autocomplete'], function(searchModule, autoco params = utils.merge(searchData, params); if (params) { + if (params.term) { + $('#search-input').val(params.term); + } + if (params.in) { $('#search-in').val(params.in); updateFormItemVisiblity(params.in); diff --git a/public/src/client/users.js b/public/src/client/users.js index 40f221e33f..17016fc7b0 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -1,16 +1,24 @@ 'use strict'; -/* globals define, socket, app, templates, bootbox, ajaxify */ +/* globals define, socket, app, templates, bootbox, utils */ define('forum/users', ['translator'], function(translator) { var Users = {}; - var loadingMoreUsers = false; + var searchTimeoutID = 0; + + $(window).on('action:ajaxify.start', function() { + if (searchTimeoutID) { + clearTimeout(searchTimeoutID); + searchTimeoutID = 0; + } + }); Users.init = function() { app.enterRoom('user_list'); - $('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + '"]').parent().addClass('active'); + var section = utils.params().section ? ('?section=' + utils.params().section) : ''; + $('.nav-pills li').removeClass('active').find('a[href="' + window.location.pathname + section + '"]').parent().addClass('active'); handleSearch(); @@ -18,110 +26,55 @@ define('forum/users', ['translator'], function(translator) { socket.removeListener('event:user_status_change', onUserStatusChange); socket.on('event:user_status_change', onUserStatusChange); - - $('#load-more-users-btn').on('click', loadMoreUsers); - - $(window).off('scroll').on('scroll', function() { - var bottom = ($(document).height() - $(window).height()) * 0.9; - - if ($(window).scrollTop() > bottom && !loadingMoreUsers) { - loadMoreUsers(); - } - }); }; - function loadMoreUsers() { - if ($('#search-user').val()) { - return; - } - - if (ajaxify.data.setName) { - startLoading(ajaxify.data.setName, $('#users-container').children('.registered-user').length); - } - } - - function startLoading(set, after) { - loadingMoreUsers = true; - - socket.emit('user.loadMore', { - set: set, - after: after - }, function(err, data) { - if (err) { - return app.alertError(err.message); - } - - if (data && data.users.length) { - onUsersLoaded(data); - $('#load-more-users-btn').removeClass('disabled'); - } else { - $('#load-more-users-btn').addClass('disabled'); - } - loadingMoreUsers = false; - }); - } - - function onUsersLoaded(data) { - data.users = data.users.filter(function(user) { - return !$('.users-box[data-uid="' + user.uid + '"]').length; - }); - - templates.parse('users', 'users', data, function(html) { - translator.translate(html, function(translated) { - translated = $(translated); - $('#users-container').append(translated); - translated.find('span.timeago').timeago(); - utils.addCommasToNumbers(translated.find('.formatted-number')); - $('#users-container .anon-user').appendTo($('#users-container')); - }); - }); - } - function handleSearch() { - var timeoutId = 0; + searchTimeoutID = 0; $('#search-user').on('keyup', function() { - if (timeoutId) { - clearTimeout(timeoutId); - timeoutId = 0; + if (searchTimeoutID) { + clearTimeout(searchTimeoutID); + searchTimeoutID = 0; } - timeoutId = setTimeout(doSearch, 250); + searchTimeoutID = setTimeout(doSearch, 150); }); $('.search select, .search input[type="checkbox"]').on('change', function() { doSearch(); }); - - $('.users').on('click', '.pagination a', function() { - doSearch($(this).attr('data-page')); - return false; - }); } function doSearch(page) { + $('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin'); var username = $('#search-user').val(); page = page || 1; - if (!username) { - return loadPage(page); - } var activeSection = getActiveSection(); - socket.emit('user.search', { - query: username, - page: page, - searchBy: 'username', - sortBy: $('.search select').val() || getSortBy(), - onlineOnly: $('.search .online-only').is(':checked') || (activeSection === 'online'), - bannedOnly: activeSection === 'banned', - flaggedOnly: activeSection === 'flagged' - }, function(err, data) { - if (err) { - return app.alertError(err.message); - } - renderSearchResults(data); - }); + var query = { + section: activeSection, + page: page + }; + + if (!username) { + return loadPage(query); + } + + query.term = username; + query.sortBy = getSortBy(); + + if ($('.search .online-only').is(':checked') || (activeSection === 'online')) { + query.onlineOnly = true; + } + if (activeSection === 'banned') { + query.bannedOnly = true; + } + if (activeSection === 'flagged') { + query.flaggedOnly = true; + } + + loadPage(query); } function getSortBy() { @@ -137,16 +90,17 @@ define('forum/users', ['translator'], function(translator) { return sortBy; } - function loadPage(page) { - var section = getActiveSection(); - section = section !== 'users' ? section : ''; - $.get('/api/users/' + section + '?page=' + page, function(data) { - renderSearchResults(data); + + function loadPage(query) { + var qs = decodeURIComponent($.param(query)); + $.get('/api/users?' + qs, renderSearchResults).fail(function(xhrErr) { + if (xhrErr && xhrErr.responseJSON && xhrErr.responseJSON.error) { + app.alertError(xhrErr.responseJSON.error); + } }); } function renderSearchResults(data) { - $('#load-more-users-btn').addClass('hide'); templates.parse('partials/paginator', {pagination: data.pagination}, function(html) { $('.pagination-container').replaceWith(html); }); @@ -156,6 +110,7 @@ define('forum/users', ['translator'], function(translator) { translated = $(translated); $('#users-container').html(translated); translated.find('span.timeago').timeago(); + $('[component="user/search/icon"]').addClass('fa-search').removeClass('fa-spinner fa-spin'); }); }); } @@ -173,9 +128,7 @@ define('forum/users', ['translator'], function(translator) { } function getActiveSection() { - var url = window.location.href; - var parts = url.split('/'); - return parts[parts.length - 1]; + return utils.params().section || ''; } function handleInvite() { diff --git a/public/src/modules/search.js b/public/src/modules/search.js index 147683c550..304235c7fa 100644 --- a/public/src/modules/search.js +++ b/public/src/modules/search.js @@ -4,8 +4,8 @@ define('search', ['navigator', 'translator'], function(nav, translator) { var Search = { - current: {} - }; + current: {} + }; Search.query = function(data, callback) { var term = data.term; @@ -22,11 +22,11 @@ define('search', ['navigator', 'translator'], function(nav, translator) { return app.alertError('[[error:invalid-search-term]]'); } - ajaxify.go('search/' + term + '?' + createQueryString(data)); + ajaxify.go('search?' + createQueryString(data)); callback(); } else { - var cleanedTerm = term.replace(topicSearch[0], ''), - tid = topicSearch[1]; + var cleanedTerm = term.replace(topicSearch[0], ''); + var tid = topicSearch[1]; if (cleanedTerm.length > 0) { Search.queryTopic(tid, cleanedTerm, callback); @@ -38,8 +38,9 @@ define('search', ['navigator', 'translator'], function(nav, translator) { var searchIn = data['in'] || 'titlesposts'; var postedBy = data.by || ''; var query = { - 'in': searchIn - }; + term: data.term, + 'in': searchIn + }; if (postedBy && (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts')) { query.by = postedBy; diff --git a/src/controllers/accounts/follow.js b/src/controllers/accounts/follow.js index f9dc72c6f3..5897347a12 100644 --- a/src/controllers/accounts/follow.js +++ b/src/controllers/accounts/follow.js @@ -1,10 +1,11 @@ 'use strict'; -var async = require('async'), +var async = require('async'); - user = require('../../user'), - helpers = require('../helpers'), - accountHelpers = require('./helpers'); +var user = require('../../user'); +var helpers = require('../helpers'); +var accountHelpers = require('./helpers'); +var pagination = require('../../pagination'); var followController = {}; @@ -19,6 +20,11 @@ followController.getFollowers = function(req, res, next) { function getFollow(tpl, name, req, res, callback) { var userData; + var page = parseInt(req.query.page, 10) || 1; + var resultsPerPage = 50; + var start = Math.max(0, page - 1) * resultsPerPage; + var stop = start + resultsPerPage - 1; + async.waterfall([ function(next) { accountHelpers.getBaseUser(req.params.userslug, req.uid, next); @@ -29,7 +35,7 @@ function getFollow(tpl, name, req, res, callback) { return callback(); } var method = name === 'following' ? 'getFollowing' : 'getFollowers'; - user[method](userData.uid, 0, 49, next); + user[method](userData.uid, start, stop, next); } ], function(err, users) { if (err) { @@ -37,8 +43,10 @@ function getFollow(tpl, name, req, res, callback) { } userData.users = users; - userData.nextStart = 50; userData.title = '[[pages:' + tpl + ', ' + userData.username + ']]'; + var count = name === 'following' ? userData.followingCount : userData.followerCount; + var pageCount = Math.ceil(count / resultsPerPage); + userData.pagination = pagination.create(page, pageCount); userData.breadcrumbs = helpers.buildBreadcrumbs([{text: userData.username, url: '/user/' + userData.userslug}, {text: '[[user:' + name + ']]'}]); res.render(tpl, userData); diff --git a/src/controllers/accounts/helpers.js b/src/controllers/accounts/helpers.js index 12a891a673..2b1cac23c3 100644 --- a/src/controllers/accounts/helpers.js +++ b/src/controllers/accounts/helpers.js @@ -133,7 +133,19 @@ helpers.getBaseUser = function(userslug, callerUID, callback) { async.parallel({ user: function(next) { - user.getUserFields(uid, ['uid', 'username', 'userslug', 'picture', 'cover:url', 'cover:position', 'status', 'lastonline', 'groupTitle'], next); + user.getUserFields(uid, [ + 'uid', + 'username', + 'userslug', + 'picture', + 'cover:url', + 'cover:position', + 'status', + 'lastonline', + 'groupTitle', + 'followingCount', + 'followerCount' + ], next); }, isAdmin: function(next) { user.isAdministrator(callerUID, next); diff --git a/src/controllers/search.js b/src/controllers/search.js index 2a7f6b145c..2466f17670 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -28,7 +28,7 @@ searchController.search = function(req, res, next) { } var data = { - query: req.params.term, + query: req.query.term, searchIn: req.query.in || 'posts', postedBy: req.query.by, categories: req.query.categories, @@ -59,7 +59,7 @@ searchController.search = function(req, res, next) { searchData.showAsTopics = req.query.showAs === 'topics'; searchData.title = '[[global:header.search]]'; searchData.breadcrumbs = helpers.buildBreadcrumbs([{text: '[[global:search]]'}]); - searchData.expandSearch = !req.params.term; + searchData.expandSearch = !req.query.term; res.render('search', searchData); }); diff --git a/src/controllers/users.js b/src/controllers/users.js index 20ea62ca40..4f503bb6a3 100644 --- a/src/controllers/users.js +++ b/src/controllers/users.js @@ -11,10 +11,61 @@ var helpers = require('./helpers'); var usersController = {}; + +usersController.index = function(req, res, next) { + var section = req.query.section || 'joindate'; + var sectionToController = { + joindate: usersController.getUsersSortedByJoinDate, + online: usersController.getOnlineUsers, + 'sort-posts': usersController.getUsersSortedByPosts, + 'sort-reputation': usersController.getUsersSortedByReputation, + banned: usersController.getBannedUsers, + flagged: usersController.getFlaggedUsers + }; + + if (req.query.term) { + usersController.search(req, res, next); + } else if (sectionToController[section]) { + sectionToController[section](req, res, next); + } else { + usersController.getUsersSortedByJoinDate(req, res, next); + } +}; + +usersController.search = function(req, res, next) { + async.parallel({ + search: function(next) { + user.search({ + query: req.query.term, + searchBy: req.query.searchBy || 'username', + page: req.query.page || 1, + sortBy: req.query.sortBy, + onlineOnly: req.query.onlineOnly === 'true', + bannedOnly: req.query.bannedOnly === 'true', + flaggedOnly: req.query.flaggedOnly === 'true' + }, next); + }, + isAdminOrGlobalMod: function(next) { + user.isAdminOrGlobalMod(req.uid, next); + } + }, function(err, results) { + if (err) { + return next(err); + } + + var section = req.query.section || 'joindate'; + + results.search.isAdminOrGlobalMod = results.isAdminOrGlobalMod; + results.search.pagination = pagination.create(req.query.page, results.search.pageCount, req.query); + results.search['section_' + section] = true; + render(req, res, results.search, next); + }); +}; + usersController.getOnlineUsers = function(req, res, next) { async.parallel({ users: function(next) { - usersController.getUsers('users:online', req.uid, req.query.page, next); + usersController.getUsers('users:online', req.uid, req.query, next); }, guests: function(next) { require('../socket.io/admin/rooms').getTotalGuestCount(next); @@ -56,7 +107,7 @@ usersController.getUsersSortedByJoinDate = function(req, res, next) { }; usersController.getBannedUsers = function(req, res, next) { - usersController.getUsers('users:banned', req.uid, req.query.page, function(err, userData) { + usersController.getUsers('users:banned', req.uid, req.query, function(err, userData) { if (err) { return next(err); } @@ -70,7 +121,7 @@ usersController.getBannedUsers = function(req, res, next) { }; usersController.getFlaggedUsers = function(req, res, next) { - usersController.getUsers('users:flags', req.uid, req.query.page, function(err, userData) { + usersController.getUsers('users:flags', req.uid, req.query, function(err, userData) { if (err) { return next(err); } @@ -84,15 +135,16 @@ usersController.getFlaggedUsers = function(req, res, next) { }; usersController.renderUsersPage = function(set, req, res, next) { - usersController.getUsers(set, req.uid, req.query.page, function(err, userData) { + usersController.getUsers(set, req.uid, req.query, function(err, userData) { if (err) { return next(err); } + render(req, res, userData, next); }); }; -usersController.getUsers = function(set, uid, page, callback) { +usersController.getUsers = function(set, uid, query, callback) { var setToData = { 'users:postcount': {title: '[[pages:users/sort-posts]]', crumb: '[[users:top_posters]]'}, 'users:reputation': {title: '[[pages:users/sort-reputation]]', crumb: '[[users:most_reputation]]'}, @@ -112,17 +164,14 @@ usersController.getUsers = function(set, uid, page, callback) { breadcrumbs.unshift({text: '[[global:users]]', url: '/users'}); } - page = parseInt(page, 10) || 1; + var page = parseInt(query.page, 10) || 1; var resultsPerPage = parseInt(meta.config.userSearchResultsPerPage, 10) || 50; var start = Math.max(0, page - 1) * resultsPerPage; var stop = start + resultsPerPage - 1; async.parallel({ - isAdministrator: function(next) { - user.isAdministrator(uid, next); - }, - isGlobalMod: function(next) { - user.isGlobalModerator(uid, next); + isAdminOrGlobalMod: function(next) { + user.isAdminOrGlobalMod(uid, next); }, usersData: function(next) { usersController.getUsersAndCount(set, uid, start, stop, next); @@ -134,16 +183,14 @@ usersController.getUsers = function(set, uid, page, callback) { var pageCount = Math.ceil(results.usersData.count / resultsPerPage); var userData = { - loadmore_display: results.usersData.count > (stop - start + 1) ? 'block' : 'hide', users: results.usersData.users, - pagination: pagination.create(page, pageCount), + pagination: pagination.create(page, pageCount, query), userCount: results.usersData.count, title: setToData[set].title || '[[pages:users/latest]]', breadcrumbs: helpers.buildBreadcrumbs(breadcrumbs), - setName: set, - isAdminOrGlobalMod: results.isAdministrator || results.isGlobalMod + isAdminOrGlobalMod: results.isAdminOrGlobalMod }; - userData['route_' + set] = true; + userData['section_' + (query.section || 'joindate')] = true; callback(null, userData); }); }; diff --git a/src/routes/index.js b/src/routes/index.js index 006e5b8c18..00e0ea0e77 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -33,7 +33,7 @@ function mainRoutes(app, middleware, controllers) { setupPageRoute(app, '/compose', middleware, [], controllers.compose); setupPageRoute(app, '/confirm/:code', middleware, [], controllers.confirmEmail); setupPageRoute(app, '/outgoing', middleware, [], controllers.outgoing); - setupPageRoute(app, '/search/:term?', middleware, [], controllers.search.search); + setupPageRoute(app, '/search', middleware, [], controllers.search.search); setupPageRoute(app, '/reset/:code?', middleware, [], controllers.reset); setupPageRoute(app, '/tos', middleware, [], controllers.termsOfUse); @@ -73,12 +73,7 @@ function categoryRoutes(app, middleware, controllers) { function userRoutes(app, middleware, controllers) { var middlewares = [middleware.checkGlobalPrivacySettings]; - setupPageRoute(app, '/users', middleware, middlewares, controllers.users.getUsersSortedByJoinDate); - setupPageRoute(app, '/users/online', middleware, middlewares, controllers.users.getOnlineUsers); - setupPageRoute(app, '/users/sort-posts', middleware, middlewares, controllers.users.getUsersSortedByPosts); - setupPageRoute(app, '/users/sort-reputation', middleware, middlewares, controllers.users.getUsersSortedByReputation); - setupPageRoute(app, '/users/banned', middleware, middlewares, controllers.users.getBannedUsers); - setupPageRoute(app, '/users/flagged', middleware, middlewares, controllers.users.getFlaggedUsers); + setupPageRoute(app, '/users', middleware, middlewares, controllers.users.index); } function groupRoutes(app, middleware, controllers) { @@ -91,15 +86,15 @@ function groupRoutes(app, middleware, controllers) { module.exports = function(app, middleware, hotswapIds) { var routers = [ - express.Router(), // plugin router - express.Router(), // main app router - express.Router() // auth router - ], - router = routers[1], - pluginRouter = routers[0], - authRouter = routers[2], - relativePath = nconf.get('relative_path'), - ensureLoggedIn = require('connect-ensure-login'); + express.Router(), // plugin router + express.Router(), // main app router + express.Router() // auth router + ]; + var router = routers[1]; + var pluginRouter = routers[0]; + var authRouter = routers[2]; + var relativePath = nconf.get('relative_path'); + var ensureLoggedIn = require('connect-ensure-login'); if (Array.isArray(hotswapIds) && hotswapIds.length) { for(var idx,x=0;x Date: Wed, 24 Aug 2016 14:52:18 +0300 Subject: [PATCH 12/86] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1f452613a3..25cc29885d 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.25", - "nodebb-theme-vanilla": "5.1.13", + "nodebb-theme-persona": "4.1.26", + "nodebb-theme-vanilla": "5.1.14", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From 6a04e2909dacf5f0932f4d65f48cb9cea2c23d56 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Wed, 24 Aug 2016 14:54:18 +0300 Subject: [PATCH 13/86] page param not used --- public/src/client/users.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/public/src/client/users.js b/public/src/client/users.js index 17016fc7b0..8f648e6198 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -45,16 +45,14 @@ define('forum/users', ['translator'], function(translator) { }); } - function doSearch(page) { + function doSearch() { $('[component="user/search/icon"]').removeClass('fa-search').addClass('fa-spinner fa-spin'); var username = $('#search-user').val(); - page = page || 1; - var activeSection = getActiveSection(); var query = { section: activeSection, - page: page + page: 1 }; if (!username) { From 15d9a5f4d59b44a0f4551964b3579dd15d46ef01 Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Wed, 24 Aug 2016 09:02:29 -0400 Subject: [PATCH 14/86] Latest translations and fallbacks --- public/language/it/error.json | 14 +++++++------- public/language/it/global.json | 4 ++-- public/language/it/groups.json | 4 ++-- public/language/it/notifications.json | 4 ++-- public/language/it/pages.json | 6 +++--- public/language/it/topic.json | 8 ++++---- public/language/it/user.json | 10 +++++----- public/language/zh_TW/modules.json | 2 +- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/public/language/it/error.json b/public/language/it/error.json index 8fd8822deb..2244b8e8a5 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -24,14 +24,14 @@ "no-email-to-confirm": "Questo forum richiede la conferma dell'indirizzo email, per favore clicca qui per inserirne uno", "email-confirm-failed": "Non possiamo confermare la tua email, per favore prova ancora più tardi.", "confirm-email-already-sent": "Email di conferma già inviata, per favore attendere %1 minuti per richiederne un'altra.", - "sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.", + "sendmail-not-found": "Non è stato possibile trovare l'eseguibile sendmail, per favore assicurati che sia installato ed eseguibile dall'utente su cui è installato NodeBB.", "username-too-short": "Nome utente troppo corto", "username-too-long": "Nome utente troppo lungo", "password-too-long": "Password troppo lunga", "user-banned": "Utente bannato", "user-too-new": "Devi attendere %1 secondi prima di creare il tuo primo post", "blacklisted-ip": "Purtroppo il tuo indirizzo IP è stato bannato da questa community. Se credi che ci sia stato un errore contatta un amministratore.", - "ban-expiry-missing": "Please provide an end date for this ban", + "ban-expiry-missing": "Per favore fornisci una data finale per questo ban", "no-category": "La Categoria non esiste", "no-topic": "La discussione non esiste", "no-post": "Il Post non esiste", @@ -70,8 +70,8 @@ "still-uploading": "Per favore attendere il completamento degli uploads.", "file-too-big": "La dimensione massima consentita è di %1 kB - si prega di caricare un file più piccolo", "guest-upload-disabled": "Il caricamento da ospite è stato disattivato", - "already-favourited": "You have already bookmarked this post", - "already-unfavourited": "You have already unbookmarked this post", + "already-favourited": "Hai già aggiunto questo post ai favoriti", + "already-unfavourited": "Hai già tolto questo post dai favoriti", "cant-ban-other-admins": "Non puoi bannare altri amministratori!", "cant-remove-last-admin": "Sei l'unico Amministratore. Aggiungi un altro amministratore prima di rimuovere il tuo ruolo", "cant-delete-admin": "Togli i privilegi amministrativi da questo account prima di provare ad eliminarlo.", @@ -117,11 +117,11 @@ "parse-error": "Qualcosa è andato storto durante l'analisi della risposta proveniente dal server", "wrong-login-type-email": "Per favore usa la tua email per accedere", "wrong-login-type-username": "Per favore usa il tuo nome utente per accedere", - "invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).", - "no-session-found": "No login session found!", + "invite-maximum-met": "Hai invitato il massimo numero di persone possibili (%1 su %2).", + "no-session-found": "Nessuna sessione valida di login trovata!", "not-in-room": "L'utente non è in questa stanza", "no-users-in-room": "Nessun utente in questa stanza", "cant-kick-self": "Non puoi espellerti dal gruppo", "no-users-selected": "Nessun utente selezionato", - "invalid-home-page-route": "Invalid home page route" + "invalid-home-page-route": "Percorso della pagina iniziale non valido" } \ No newline at end of file diff --git a/public/language/it/global.json b/public/language/it/global.json index ccfff6ad6e..0bebb5cd9e 100644 --- a/public/language/it/global.json +++ b/public/language/it/global.json @@ -51,7 +51,7 @@ "posts": "Post", "best": "Migliore", "upvoters": "Upvoters", - "upvoted": "Upvoted", + "upvoted": "Apprezzati", "downvoters": "Downvoters", "downvoted": "Downvoted", "views": "Visualizzazioni", @@ -92,6 +92,6 @@ "enter_page_number": "Inserisci il numero della pagina", "upload_file": "Carica file", "upload": "Carica", - "allowed-file-types": "Allowed file types are %1", + "allowed-file-types": "Le estensioni permesse dei file sono %1", "unsaved-changes": "Hai delle modifiche non salvate. Sei sicuro che vuoi lasciare la pagina?" } \ No newline at end of file diff --git a/public/language/it/groups.json b/public/language/it/groups.json index 863b4be850..45cc2d894b 100644 --- a/public/language/it/groups.json +++ b/public/language/it/groups.json @@ -24,7 +24,7 @@ "details.has_no_posts": "I membri di questo gruppo non hanno ancora postato.", "details.latest_posts": "Ultimi Post", "details.private": "Privato", - "details.disableJoinRequests": "Disable join requests", + "details.disableJoinRequests": "Disabilita le richieste d'adesione", "details.grant": "Concedi / Rimuovi la Proprietà", "details.kick": "Espelli", "details.owner_options": "Amministratore del Grupo", @@ -41,7 +41,7 @@ "details.hidden": "Nascosto", "details.hidden_help": "Se abilitato, questo gruppo non sarà visibile nella lista dei gruppi e gli utenti dovranno essere invitati manualmente", "details.delete_group": "Elimina il Gruppo", - "details.private_system_help": "Private groups is disabled at system level, this option does not do anything", + "details.private_system_help": "I gruppi privati sono disabilitati dal livello del sistema, questa opzione non fa nulla", "event.updated": "I dettagli del Gruppo sono stati aggiornati", "event.deleted": "Il gruppo \"%1\" è stato eliminato", "membership.accept-invitation": "Accetta l'invito", diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index c7725575b5..82fa25deec 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -17,8 +17,8 @@ "moved_your_post": "%1 ha spostato il tuo post su %2", "moved_your_topic": "%1 è stato spostato %2", "user_flagged_post_in": "%1 ha segnalato un post in %2", - "user_flagged_post_in_dual": "%1 and %2 flagged a post in %3", - "user_flagged_post_in_multiple": "%1 and %2 others flagged a post in %3", + "user_flagged_post_in_dual": "%1 e %2 hanno segnalato un post in %3", + "user_flagged_post_in_multiple": "%1 ed altri %2 hanno segnalato un post in %3", "user_posted_to": "%1 ha postato una risposta a: %2", "user_posted_to_dual": "%1 e %2 hanno postato una risposta su: %3", "user_posted_to_multiple": "%1 ed altri %2 hanno postato una risposta su: %3", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index e509b23ef8..b0920945fc 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -6,13 +6,13 @@ "popular-month": "Discussioni popolari questo mese", "popular-alltime": "Discussioni più popolari di sempre", "recent": "Discussioni Recenti", - "flagged-posts": "Flagged Posts", + "flagged-posts": "Post Segnalati", "users/online": "Utenti Online", "users/latest": "Ultimi Utenti", "users/sort-posts": "Utenti maggiori contributori", "users/sort-reputation": "Utenti con la reputazione più alta", "users/banned": "Utenti Bannati", - "users/most-flags": "Most flagged users", + "users/most-flags": "Gli utenti più segnalati", "users/search": "Ricerca Utenti", "notifications": "Notifiche", "tags": "Tags", @@ -29,7 +29,7 @@ "account/edit/password": "Modificando la password di \"%1\"", "account/edit/username": "Modificando il nome utente di \"%1\"", "account/edit/email": "Modificando l'email di \"%1\"", - "account/info": "Account Info", + "account/info": "Informazioni dell'account", "account/following": "Persone seguite da %1", "account/followers": "Persone che seguono %1", "account/posts": "Post creati da %1", diff --git a/public/language/it/topic.json b/public/language/it/topic.json index 5367678fa8..4ff8c7cdc9 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -47,7 +47,7 @@ "watching": "Seguito", "not-watching": "Non Seguito", "ignoring": "Ignorato", - "watching.description": "Notify me of new replies.
Show topic in unread.", + "watching.description": "Notificami sulle nuove risposte.
Mostra la discussione tra le non lette.", "not-watching.description": "Non notificarmi sulle nuove risposte.
Mostra la discussione fra le non lette se la categoria non è ignorata.", "ignoring.description": "Non notificarmi sulle nuove risposte.
Non mostrare la discussione fra le non lette.", "thread_tools.title": "Strumenti per la Discussione", @@ -74,9 +74,9 @@ "disabled_categories_note": "Le Categorie disabilitate sono in grigio", "confirm_move": "Sposta", "confirm_fork": "Dividi", - "favourite": "Bookmark", - "favourites": "Bookmarks", - "favourites.has_no_favourites": "You haven't bookmarked any posts yet.", + "favourite": "Favorito", + "favourites": "Favoriti", + "favourites.has_no_favourites": "Non hai ancora aggiunto nessun post tra i favoriti", "loading_more_posts": "Caricamento altri post", "move_topic": "Sposta Discussione", "move_topics": "Sposta Discussioni", diff --git a/public/language/it/user.json b/public/language/it/user.json index 939f0e0923..355e6e84be 100644 --- a/public/language/it/user.json +++ b/public/language/it/user.json @@ -80,7 +80,7 @@ "has_no_posts": "Questo utente non ha ancora scritto niente.", "has_no_topics": "Questo utente non ha ancora avviato discussioni.", "has_no_watched_topics": "Questo utente non sta osservando discussioni.", - "has_no_upvoted_posts": "This user hasn't upvoted any posts yet.", + "has_no_upvoted_posts": "Questo utente non ha ancora apprezzato nessun post.", "has_no_downvoted_posts": "This user hasn't downvoted any posts yet.", "has_no_voted_posts": "Questo utente non ha post votati", "email_hidden": "Email Nascosta", @@ -104,13 +104,13 @@ "select-homepage": "Seleziona una Pagina Iniziale", "homepage": "Pagina iniziale", "homepage_description": "Seleziona una pagina da usare come pagina iniziale o \"Nessuna\" per usare quella di default.", - "custom_route": "Custom Homepage Route", - "custom_route_help": "Enter a route name here, without any preceding slash (e.g. \"recent\", or \"popular\")", + "custom_route": "Percorso della Homepage personalizzato", + "custom_route_help": "Inserisci qui un nome percorso, senza nessuno slash precedente (p.es. \"recent\", o \"popular\")", "sso.title": "Servizi Single-Sign-On", "sso.associated": "Associa con", "sso.not-associated": "Clicca qui per associare con", - "info.latest-flags": "Latest Flags", - "info.no-flags": "No Flagged Posts Found", + "info.latest-flags": "Ultime Segnalazioni", + "info.no-flags": "Non è stato trovato nessun post segnalato", "info.ban-history": "Storico dei Ban recenti", "info.no-ban-history": "Questo utente non è mai stato bannato", "info.banned-until": "Bannato fino %1", diff --git a/public/language/zh_TW/modules.json b/public/language/zh_TW/modules.json index e02c0908f1..61d9b00cea 100644 --- a/public/language/zh_TW/modules.json +++ b/public/language/zh_TW/modules.json @@ -37,7 +37,7 @@ "composer.formatting.picture": "圖片", "composer.upload-picture": "上傳圖片", "composer.upload-file": "上傳檔案", - "composer.zen_mode": "Zen Mode", + "composer.zen_mode": "禪(Zen)模式", "bootbox.ok": "好", "bootbox.cancel": "取消", "bootbox.confirm": "確認", From 358deb386a555a2d65d9b94f6e963da1f232f981 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 24 Aug 2016 11:14:15 -0400 Subject: [PATCH 15/86] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 25cc29885d..a319e90b61 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.26", - "nodebb-theme-vanilla": "5.1.14", + "nodebb-theme-persona": "4.1.27", + "nodebb-theme-vanilla": "5.1.15", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From f0c4c5979341089817dc3af28427732cdaccc0e3 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 24 Aug 2016 16:12:00 -0400 Subject: [PATCH 16/86] up composer --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a319e90b61..93b1c4cbf1 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "morgan": "^1.3.2", "mousetrap": "^1.5.3", "nconf": "~0.8.2", - "nodebb-plugin-composer-default": "4.1.8", + "nodebb-plugin-composer-default": "4.1.9", "nodebb-plugin-dbsearch": "1.0.2", "nodebb-plugin-emoji-extended": "1.1.1", "nodebb-plugin-emoji-one": "1.1.5", From ed3bc06dee1b8fa8ce37100c9b7ab3c90a1ecf97 Mon Sep 17 00:00:00 2001 From: Timothy Fike Date: Wed, 24 Aug 2016 17:48:04 -0400 Subject: [PATCH 17/86] Fix requiring files outside of node_modules e.g. ``` "scripts": [ "../../public/vendor/jquery/js/jquery-ui-1.10.4.custom.js" ] ``` --- src/plugins/load.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/load.js b/src/plugins/load.js index 99019e371f..8eac293829 100644 --- a/src/plugins/load.js +++ b/src/plugins/load.js @@ -276,7 +276,8 @@ module.exports = function(Plugins) { * With npm@3, dependencies can become flattened, and appear at the root level. * This method resolves these differences if it can. */ - var atRootLevel = fullPath.match(/node_modules/g).length === 1; + var matches = fullPath.match(/node_modules/g); + var atRootLevel = !matches || matches.length === 1; try { fs.statSync(fullPath); @@ -329,4 +330,4 @@ module.exports = function(Plugins) { } }); }; -}; \ No newline at end of file +}; From 9eb64aa3fa718b750e6a8826628515a276fbc4ce Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 10:30:46 +0300 Subject: [PATCH 18/86] closes #4975 --- public/src/ajaxify.js | 6 +---- public/src/client/account/edit.js | 38 +++++++++++++++---------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index d0592d8511..bec556c07a 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -216,11 +216,7 @@ $(document).ready(function() { return url; }; - ajaxify.refresh = function(e, callback) { - if (e && e instanceof jQuery.Event) { - e.preventDefault(); - } - + ajaxify.refresh = function(callback) { ajaxify.go(ajaxify.currentPage + window.location.search + window.location.hash, callback, true); }; diff --git a/public/src/client/account/edit.js b/public/src/client/account/edit.js index db600a2f55..6cbea282d0 100644 --- a/public/src/client/account/edit.js +++ b/public/src/client/account/edit.js @@ -2,12 +2,10 @@ /* globals define, ajaxify, socket, app, config, templates, bootbox */ -define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function(header, uploader, translator) { - var AccountEdit = {}, - uploadedPicture = ''; +define('forum/account/edit', ['forum/account/header', 'uploader', 'translator', 'components'], function(header, uploader, translator, components) { + var AccountEdit = {}; AccountEdit.init = function() { - uploadedPicture = ajaxify.data.uploadedpicture; header.init(); @@ -59,17 +57,15 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], } function updateHeader(picture) { - require(['components'], function(components) { - if (parseInt(ajaxify.data.theirid, 10) !== parseInt(ajaxify.data.yourid, 10)) { - return; - } + if (parseInt(ajaxify.data.theirid, 10) !== parseInt(ajaxify.data.yourid, 10)) { + return; + } - components.get('header/userpicture')[picture ? 'show' : 'hide'](); - components.get('header/usericon')[!picture ? 'show' : 'hide'](); - if (picture) { - components.get('header/userpicture').attr('src', picture); - } - }); + components.get('header/userpicture')[picture ? 'show' : 'hide'](); + components.get('header/usericon')[!picture ? 'show' : 'hide'](); + if (picture) { + components.get('header/userpicture').attr('src', picture); + } } function handleImageChange() { @@ -137,8 +133,8 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], } function saveSelection() { - var type = modal.find('.list-group-item.active').attr('data-type'), - src = modal.find('.list-group-item.active img').attr('src'); + var type = modal.find('.list-group-item.active').attr('data-type'); + changeUserPicture(type, function(err) { if (err) { return app.alertError(err.message); @@ -176,7 +172,7 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], if (err) { return app.alertError(err.message); } - + window.location.href = config.relative_path + '/'; }); } @@ -192,15 +188,17 @@ define('forum/account/edit', ['forum/account/header', 'uploader', 'translator'], function handleImageUpload(modal) { function onUploadComplete(urlOnServer) { - urlOnServer = urlOnServer + '?' + new Date().getTime(); + urlOnServer = urlOnServer + '?' + Date.now(); updateHeader(urlOnServer); if (ajaxify.data.picture.length) { $('#user-current-picture, img.avatar').attr('src', urlOnServer); - uploadedPicture = urlOnServer; + ajaxify.data.uploadedpicture = urlOnServer; } else { - ajaxify.refresh(); + ajaxify.refresh(function() { + $('#user-current-picture, img.avatar').attr('src', urlOnServer); + }); } } From b6ac809a7dbb89f8cd1bbcb78c59f5ba3f342457 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 13:56:43 +0300 Subject: [PATCH 19/86] moved sync code out of async --- src/user/picture.js | 49 ++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/src/user/picture.js b/src/user/picture.js index 78ad131ab4..fcac5c5c6f 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -1,20 +1,20 @@ 'use strict'; -var async = require('async'), - path = require('path'), - fs = require('fs'), - os = require('os'), - nconf = require('nconf'), - crypto = require('crypto'), - winston = require('winston'), - request = require('request'), - mime = require('mime'), +var async = require('async'); +var path = require('path'); +var fs = require('fs'); +var os = require('os'); +var nconf = require('nconf'); +var crypto = require('crypto'); +var winston = require('winston'); +var request = require('request'); +var mime = require('mime'); - plugins = require('../plugins'), - file = require('../file'), - image = require('../image'), - meta = require('../meta'), - db = require('../database'); +var plugins = require('../plugins'); +var file = require('../file'); +var image = require('../image'); +var meta = require('../meta'); +var db = require('../database'); module.exports = function(User) { @@ -28,16 +28,19 @@ module.exports = function(User) { var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; var uploadedImage; + if (parseInt(meta.config.allowProfileImageUploads) !== 1) { + return callback(new Error('[[error:profile-image-uploads-disabled]]')); + } + + if (picture.size > uploadSize * 1024) { + return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); + } + + if (!extension) { + return callback(new Error('[[error:invalid-image-extension]]')); + } + async.waterfall([ - function(next) { - next(parseInt(meta.config.allowProfileImageUploads) !== 1 ? new Error('[[error:profile-image-uploads-disabled]]') : null); - }, - function(next) { - next(picture.size > uploadSize * 1024 ? new Error('[[error:file-too-big, ' + uploadSize + ']]') : null); - }, - function(next) { - next(!extension ? new Error('[[error:invalid-image-extension]]') : null); - }, function(next) { if (plugins.hasListeners('filter:uploadImage')) { return plugins.fireHook('filter:uploadImage', {image: picture, uid: updateUid}, next); From abd0a1091800ca4823519feae747ee3137bec53c Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 14:24:17 +0300 Subject: [PATCH 20/86] added selected group to profile api --- src/controllers/accounts/profile.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index 74e919d8f5..2f257def2c 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -119,6 +119,10 @@ profileController.get = function(req, res, callback) { ); } + userData.selectedGroup = userData.groups.find(function(group) { + return group && group.name === userData.groupTitle; + }); + plugins.fireHook('filter:user.account', {userData: userData, uid: req.uid}, next); } ], function(err, results) { From 5393a98e2630c35718ef718f06f2c7ec4c1f4f1e Mon Sep 17 00:00:00 2001 From: NodeBB Misty Date: Thu, 25 Aug 2016 09:02:33 -0400 Subject: [PATCH 21/86] Latest translations and fallbacks --- public/language/gl/category.json | 4 ++-- public/language/gl/error.json | 12 ++++++------ public/language/gl/notifications.json | 2 +- public/language/it/error.json | 2 +- public/language/it/topic.json | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/language/gl/category.json b/public/language/gl/category.json index eb12b9660e..aeafc9f8e0 100644 --- a/public/language/gl/category.json +++ b/public/language/gl/category.json @@ -14,7 +14,7 @@ "ignoring": "Ignorando", "watching.description": "Amosa-los temas en \"non lidos\"", "ignoring.description": "Non amosa-los temas en \"non lidos\"", - "watch.message": "Agora Sigues as novidades desta categoría", - "ignore.message": "Agora ignoras as novidades nesta categoría", + "watch.message": "Agora segues as novidades desta categoría.", + "ignore.message": "Agora ignoras as novidades nesta categoría.", "watched-categories": "Categorías vixiadas" } \ No newline at end of file diff --git a/public/language/gl/error.json b/public/language/gl/error.json index d7cf7fa7da..d372ec2b2e 100644 --- a/public/language/gl/error.json +++ b/public/language/gl/error.json @@ -2,13 +2,13 @@ "invalid-data": "Datos non válidos", "not-logged-in": "Parece que estás desconectado.", "account-locked": "A túa conta foi bloqueada temporalmente.", - "search-requires-login": "As buscas requiren unha conta - por favor inicia sesión ou rexístrate.", + "search-requires-login": "As buscas requiren unha conta. Por favor inicia sesión ou rexístrate.", "invalid-cid": "Identificador de Categoría Inválido ", "invalid-tid": "Identificador de Tema Inválido", "invalid-pid": "Identificador de Publicación Inválido", "invalid-uid": "Identificador de Usuario Inválido", "invalid-username": "Nome de Usuario Inválido", - "invalid-email": "Correo electrónico inválido", + "invalid-email": "Enderezo electrónico inválido", "invalid-title": "Título inválido!", "invalid-user-data": "Datos de Usuario Inválidos", "invalid-password": "Contrasinal Inválido", @@ -17,12 +17,12 @@ "csrf-invalid": "Non fomos capaces de entrar, probablemente debido a que a sesión expirou. Por favor, téntao de novo", "invalid-pagination-value": "Valor de paxinación incorreto, ten que estar entre %1 e %2", "username-taken": "Nome de usuario en uso", - "email-taken": "Correo en uso", + "email-taken": "Enderezo electrónico en uso", "email-not-confirmed": "O teu correo aínda non está confirmado, por favor pica aquí para confirmalo.", "email-not-confirmed-chat": "Non podes charlar ata que confirmes o teu correo, por favor pica aquí para confirmalo.", - "email-not-confirmed-email-sent": "O teu correo electrónico está sen confirmar. Por favor, busca o correo de confirmación na túa bandexa de entrada.", - "no-email-to-confirm": "Este foro require confirmación de correo, por favor pica aquí para introducir un correo.", - "email-confirm-failed": "Non podemos confirmar o teu correo, por favor téntao de novo máis tarde.", + "email-not-confirmed-email-sent": "O teu enderezo electrónico está sen confirmar. Por favor, busca o correo de confirmación na túa bandexa de entrada.", + "no-email-to-confirm": "O foro require confirmación de enderezo electrónico, por favor pica aquí para engadir un.", + "email-confirm-failed": "Non podemos confirmar o teu enderezo, por favor téntao de novo máis tarde.", "confirm-email-already-sent": "O correo de confirmación foi enviado, agarda %1 minute(s) para enviar outro.", "sendmail-not-found": "Non se atopa o executable \"sendmail\", por favor, asegúrate de que está instalado no teu sistema e que é accesible polo usuario que executa NodeBB. ", "username-too-short": "Nome de usuario demasiado curto", diff --git a/public/language/gl/notifications.json b/public/language/gl/notifications.json index deeb2835a5..326680a2fd 100644 --- a/public/language/gl/notifications.json +++ b/public/language/gl/notifications.json @@ -15,7 +15,7 @@ "upvoted_your_post_in_dual": "%1 e %2 votaron positivamente a túa mensaxe en %3.", "upvoted_your_post_in_multiple": "%1 e %2 máis votaron positivamente a túa mensaxe en %3.", "moved_your_post": "%1 moveu a túa publicación a%2", - "moved_your_topic": "%1 foi movido %2", + "moved_your_topic": "%1 moveu %2", "user_flagged_post_in": "%1 reportou unha mensaxe en %2", "user_flagged_post_in_dual": "%1 e %2 reportaron a túa mensaxe en %3", "user_flagged_post_in_multiple": "%1 e outras %2 persoas reportaron unha mensaxe en %3", diff --git a/public/language/it/error.json b/public/language/it/error.json index 2244b8e8a5..4412fbc484 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -89,7 +89,7 @@ "group-already-requested": "La tua richiesta di partecipazione è già stata inviata", "post-already-deleted": "Questo Post è già stato cancellato", "post-already-restored": "Questo Post è già stato ripristinato", - "topic-already-deleted": "Questo Topic è già stato cancellato", + "topic-already-deleted": "Questo topic è già stato eliminato", "topic-already-restored": "Questo Topic è già stato ripristinato", "cant-purge-main-post": "Non puoi svuotare il primo messaggio, elimina invece l'intera discussione", "topic-thumbnails-are-disabled": "Le anteprime della Discussione sono disabilitate.", diff --git a/public/language/it/topic.json b/public/language/it/topic.json index 4ff8c7cdc9..f44a429712 100644 --- a/public/language/it/topic.json +++ b/public/language/it/topic.json @@ -5,7 +5,7 @@ "no_topics_found": "Nessuna discussione trovata!", "no_posts_found": "Nessun post trovato!", "post_is_deleted": "Questo post è eliminato!", - "topic_is_deleted": "Questa discussione é stata eliminata", + "topic_is_deleted": "Questa discussione è stata eliminata", "profile": "Profilo", "posted_by": "Pubblicato da: %1", "posted_by_guest": "Scritto da Ospite", From 276031cd6ff554a98287c5eacf58a6f8ddce7a51 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 16:05:02 +0300 Subject: [PATCH 22/86] closes #4585, closes https://github.com/NodeBB/nodebb-theme-persona/issues/299 --- public/src/app.js | 4 ++-- public/src/modules/chat.js | 5 ++--- src/messaging.js | 30 ++++++++++++++++-------------- src/messaging/rooms.js | 18 ++++++++++++++++++ 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/public/src/app.js b/public/src/app.js index 66257e7980..e66d0d03c2 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -189,12 +189,12 @@ app.cacheBuster = null; } } - app.createUserTooltips = function(els) { + app.createUserTooltips = function(els, placement) { els = els || $('body'); els.find('.avatar,img[title].teaser-pic,img[title].user-img,div.user-icon,span.user-icon').each(function() { if (!utils.isTouchDevice()) { $(this).tooltip({ - placement: 'top', + placement: placement || $(this).attr('title-placement') || 'top', title: $(this).attr('title') }); } diff --git a/public/src/modules/chat.js b/public/src/modules/chat.js index 445b4212e4..8e0b435434 100644 --- a/public/src/modules/chat.js +++ b/public/src/modules/chat.js @@ -113,13 +113,12 @@ define('chat', [ return room.teaser; }); - chatsListEl.empty(); - templates.parse('partials/chat_dropdown', { rooms: rooms }, function(html) { translator.translate(html, function(translated) { - chatsListEl.html(translated); + chatsListEl.empty().html(translated); + app.createUserTooltips(chatsListEl, 'right'); }); }); }); diff --git a/src/messaging.js b/src/messaging.js index 1ce4160d62..b17bea6ec2 100644 --- a/src/messaging.js +++ b/src/messaging.js @@ -242,6 +242,9 @@ var async = require('async'), } async.parallel({ + roomData: function(next) { + Messaging.getRoomsData(roomIds, next); + }, unread: function(next) { db.isSortedSetMembers('uid:' + uid + ':chat:rooms:unread', roomIds, next); }, @@ -254,7 +257,7 @@ var async = require('async'), uids = uids.filter(function(value) { return value && parseInt(value, 10) !== parseInt(uid, 10); }); - user.getUsersFields(uids, ['uid', 'username', 'picture', 'status', 'lastonline'] , next); + user.getUsersFields(uids, ['uid', 'username', 'userslug', 'picture', 'status', 'lastonline'] , next); }); }, next); }, @@ -267,29 +270,28 @@ var async = require('async'), if (err) { return callback(err); } - var rooms = results.users.map(function(users, index) { - var data = { - users: users, - unread: results.unread[index], - roomId: roomIds[index], - teaser: results.teasers[index] - }; - data.users.forEach(function(userData) { + + results.roomData.forEach(function(room, index) { + room.users = results.users[index]; + room.unread = results.unread[index]; + room.teaser = results.teasers[index]; + + room.users.forEach(function(userData) { if (userData && parseInt(userData.uid, 10)) { userData.status = user.getStatus(userData); } }); - data.users = data.users.filter(function(user) { + room.users = room.users.filter(function(user) { return user && parseInt(user.uid, 10); }); - data.lastUser = data.users[0]; - data.usernames = data.users.map(function(user) { + room.lastUser = room.users[0]; + + room.usernames = room.users.map(function(user) { return user.username; }).join(', '); - return data; }); - callback(null, {rooms: rooms, nextStart: stop + 1}); + callback(null, {rooms: results.roomData, nextStart: stop + 1}); }); }); }; diff --git a/src/messaging/rooms.js b/src/messaging/rooms.js index 0c9c5bb01f..8c021d509e 100644 --- a/src/messaging/rooms.js +++ b/src/messaging/rooms.js @@ -21,6 +21,24 @@ module.exports = function(Messaging) { }); }; + Messaging.getRoomsData = function(roomIds, callback) { + var keys = roomIds.map(function(roomId) { + return 'chat:room:' + roomId; + }); + db.getObjects(keys, function(err, roomData) { + if (err) { + return callback(err); + } + roomData.forEach(function(data) { + if (data) { + data.roomName = data.roomName || '[[modules:chat.roomname, ' + data.roomId + ']]'; + data.roomName = validator.escape(String(data.roomName)); + } + }); + callback(null, roomData); + }); + }; + Messaging.newRoom = function(uid, toUids, callback) { var roomId; var now = Date.now(); From bb6a3f3a522b4e28b89681a8b80ce9adcf4eeb25 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 16:26:54 +0300 Subject: [PATCH 23/86] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 93b1c4cbf1..d5cfbd0450 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.27", - "nodebb-theme-vanilla": "5.1.15", + "nodebb-theme-persona": "4.1.28", + "nodebb-theme-vanilla": "5.1.16", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From 59832358758c4a2538f2481e5670f8c33a369253 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 16:40:18 +0300 Subject: [PATCH 24/86] .translateAttr --- public/src/overrides.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/public/src/overrides.js b/public/src/overrides.js index 62eca1d568..49dce7e754 100644 --- a/public/src/overrides.js +++ b/public/src/overrides.js @@ -68,6 +68,15 @@ if ('undefined' !== typeof window) { return translate(this, 'val', str); }; + $.fn.translateAttr = function(attr, str) { + return this.each(function() { + var el = $(this); + translator.translate(str, function(translated) { + el.attr(attr, translated); + }); + }); + }; + function translate(elements, type, str) { return elements.each(function() { var el = $(this); @@ -107,7 +116,7 @@ if ('undefined' !== typeof window) { var dialog = bootbox.dialog, prompt = bootbox.prompt, confirm = bootbox.confirm; - + function translate(modal) { var header = modal.find('.modal-header'), footer = modal.find('.modal-footer'); From ad0dd3bf289223b8d28ff034924dfedb4598ac67 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 16:56:13 +0300 Subject: [PATCH 25/86] up theme --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d5cfbd0450..77fd981b9d 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.28", + "nodebb-theme-persona": "4.1.29", "nodebb-theme-vanilla": "5.1.16", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", From f996dfab9130a18443cd59e2602bc2bf3c6647ba Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 17:02:22 +0300 Subject: [PATCH 26/86] removed find --- src/controllers/accounts/profile.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/controllers/accounts/profile.js b/src/controllers/accounts/profile.js index 2f257def2c..de10c69b6a 100644 --- a/src/controllers/accounts/profile.js +++ b/src/controllers/accounts/profile.js @@ -119,8 +119,10 @@ profileController.get = function(req, res, callback) { ); } - userData.selectedGroup = userData.groups.find(function(group) { - return group && group.name === userData.groupTitle; + userData.groups.forEach(function(group) { + if (group && group.name === userData.groupTitle) { + userData.selectedGroup = group; + } }); plugins.fireHook('filter:user.account', {userData: userData, uid: req.uid}, next); From 6c8a34ae50ab02212ca43dad0e09e5bcc24518d1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 17:32:26 +0300 Subject: [PATCH 27/86] up persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77fd981b9d..f795174f97 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.29", + "nodebb-theme-persona": "4.1.30", "nodebb-theme-vanilla": "5.1.16", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", From da47f5675cc4b5990c5a6a7a4649b95b1510c272 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 25 Aug 2016 13:00:59 -0400 Subject: [PATCH 28/86] don't wrap images in links if the link is blank, fixes #4976 --- public/src/client/topic/posts.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 656364b3a1..c6dfaaaaa6 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -335,6 +335,10 @@ define('forum/topic/posts', [ src = $this.attr('src'), suffixRegex = /-resized(\.[\w]+)?$/; + if (src === 'about:blank') { + return; + } + if (utils.isRelativeUrl(src) && suffixRegex.test(src)) { src = src.replace(suffixRegex, '$1'); } From 4dd5d1877f11e227fad2f0ba707cb5e33129d546 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 25 Aug 2016 14:11:22 -0400 Subject: [PATCH 29/86] update themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f795174f97..99a0a2354e 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.30", - "nodebb-theme-vanilla": "5.1.16", + "nodebb-theme-persona": "4.1.31", + "nodebb-theme-vanilla": "5.1.17", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From aa45db53f955d2240a50bffc6dbc97890f1b2f71 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 25 Aug 2016 14:23:53 -0400 Subject: [PATCH 30/86] update persona --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 99a0a2354e..c4d3a5d945 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.31", + "nodebb-theme-persona": "4.1.32", "nodebb-theme-vanilla": "5.1.17", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", From f7540a7edca502eb103b5ce55550ca9a26ac15fe Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 21:56:13 +0300 Subject: [PATCH 31/86] closes #4811 --- public/language/en_GB/global.json | 3 ++- public/src/ajaxify.js | 3 ++- public/src/sockets.js | 6 +++++- src/middleware/header.js | 6 +++++- src/views/500-embed.tpl | 5 ++--- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/public/language/en_GB/global.json b/public/language/en_GB/global.json index b8cadf0bc6..66ea4af420 100644 --- a/public/language/en_GB/global.json +++ b/public/language/en_GB/global.json @@ -120,5 +120,6 @@ "upload": "Upload", "allowed-file-types": "Allowed file types are %1", - "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?" + "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?", + "reconnecting-message": "Looks like your connection to %1 was lost, please wait while we try to reconnect." } diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index bec556c07a..8237a9961b 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -375,7 +375,8 @@ $(document).ready(function() { app.load(); $('[data-template]').each(function() { - templates.cache[$(this).attr('data-template')] = $(this).html(); + templates.cache[$(this).attr('data-template')] = $('
').html($(this).html()).text(); + $(this).parent().remove(); }); }); \ No newline at end of file diff --git a/public/src/sockets.js b/public/src/sockets.js index f2a9bfd346..15eee26b5e 100644 --- a/public/src/sockets.js +++ b/public/src/sockets.js @@ -48,9 +48,11 @@ app.isConnected = false; if (reconnecting) { var reconnectEl = $('#reconnect'); + var reconnectAlert = $('#reconnect-alert'); reconnectEl.tooltip('destroy'); reconnectEl.html(''); + reconnectAlert.fadeOut(500); reconnecting = false; reJoinCurrentRoom(); @@ -102,12 +104,14 @@ app.isConnected = false; function onReconnecting() { reconnecting = true; var reconnectEl = $('#reconnect'); + var reconnectAlert = $('#reconnect-alert'); if (!reconnectEl.hasClass('active')) { reconnectEl.html(''); + reconnectAlert.fadeIn(500).removeClass('hide'); } - reconnectEl.addClass('active').removeClass("hide").tooltip({ + reconnectEl.addClass('active').removeClass('hide').tooltip({ placement: 'bottom' }); } diff --git a/src/middleware/header.js b/src/middleware/header.js index 4f25a55ca5..6425c17d74 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -2,6 +2,7 @@ var async = require('async'); var nconf = require('nconf'); +var validator = require('validator'); var db = require('../database'); var user = require('../user'); @@ -27,7 +28,10 @@ module.exports = function(app, middleware) { controllers.api.getConfig(req, res, next); }, footer: function(next) { - app.render('footer', {loggedIn: (req.user ? parseInt(req.user.uid, 10) !== 0 : false)}, next); + app.render('footer', { + loggedIn: !!req.uid, + title: validator.escape(meta.config.title || meta.config.browserTitle || 'NodeBB') + }, next); }, plugins: function(next) { plugins.fireHook('filter:middleware.buildHeader', {req: req, locals: res.locals}, next); diff --git a/src/views/500-embed.tpl b/src/views/500-embed.tpl index 537cbac136..4d4dd6fc34 100644 --- a/src/views/500-embed.tpl +++ b/src/views/500-embed.tpl @@ -2,8 +2,7 @@
[[global:500.title]]

[[global:500.message]]

-

{path}

-

{error}

- +

{path}

+ <!-- IF error -->

{error}

<!-- ENDIF error -->
\ No newline at end of file From 056942ea4d2f16eb7f706579fc8485c8bac51f5b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 22:01:56 +0300 Subject: [PATCH 32/86] up themes --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c4d3a5d945..b498b13924 100644 --- a/package.json +++ b/package.json @@ -57,8 +57,8 @@ "nodebb-plugin-spam-be-gone": "0.4.10", "nodebb-rewards-essentials": "0.0.9", "nodebb-theme-lavender": "3.0.13", - "nodebb-theme-persona": "4.1.32", - "nodebb-theme-vanilla": "5.1.17", + "nodebb-theme-persona": "4.1.33", + "nodebb-theme-vanilla": "5.1.18", "nodebb-widget-essentials": "2.0.10", "nodemailer": "2.0.0", "nodemailer-sendmail-transport": "1.0.0", From 254428726902628be1014443cbd5263212d975cb Mon Sep 17 00:00:00 2001 From: barisusakli Date: Thu, 25 Aug 2016 22:16:54 +0300 Subject: [PATCH 33/86] closes #4977 --- public/less/admin/general/navigation.less | 3 ++- public/src/admin/general/navigation.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/public/less/admin/general/navigation.less b/public/less/admin/general/navigation.less index e56f4cc409..86fd3cae62 100644 --- a/public/less/admin/general/navigation.less +++ b/public/less/admin/general/navigation.less @@ -1,7 +1,8 @@ #navigation { #active-navigation { width: 100%; - + min-height: 50px; + border: 1px solid #eee; .active { background-color: #eee; } diff --git a/public/src/admin/general/navigation.js b/public/src/admin/general/navigation.js index 94fbe01549..9ebdce555a 100644 --- a/public/src/admin/general/navigation.js +++ b/public/src/admin/general/navigation.js @@ -66,7 +66,7 @@ define('admin/general/navigation', ['translator', 'iconSelect'], function(transl data = id === 'custom' ? {iconClass: 'fa-navicon'} : available[id]; data.enabled = false; - data.index = parseInt($('#enabled').children().last().attr('data-index'), 10) + 1; + data.index = (parseInt($('#enabled').children().last().attr('data-index'), 10) || 0) + 1; templates.parse('admin/general/navigation', 'navigation', {navigation: [data]}, function(li) { li = $(translator.unescape(li)); From 2428d5e44217e77abefbf4af9763e4251a1e5d8d Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 25 Aug 2016 16:10:06 -0400 Subject: [PATCH 34/86] Update bootstrap in ACP Also fixed #4872 by moving user creation modal to root level (outside of .panel) --- public/less/admin/bootstrap/bootstrap.less | 4 +- .../less/admin/bootstrap/button-groups.less | 2 +- public/less/admin/bootstrap/forms.less | 2 +- public/less/admin/bootstrap/input-groups.less | 2 +- .../admin/bootstrap/mixins/tab-focus.less | 6 +- public/less/admin/bootstrap/panels.less | 2 +- public/less/admin/bootstrap/scaffolding.less | 2 +- public/less/admin/bootstrap/theme.less | 4 +- public/less/admin/bootstrap/variables.less | 184 +++++++++--------- public/src/admin/manage/users.js | 75 ++++--- src/meta/css.js | 3 +- src/views/admin/manage/users.tpl | 40 ---- .../admin/partials/create_user_modal.tpl | 21 ++ 13 files changed, 174 insertions(+), 173 deletions(-) create mode 100644 src/views/admin/partials/create_user_modal.tpl diff --git a/public/less/admin/bootstrap/bootstrap.less b/public/less/admin/bootstrap/bootstrap.less index 1c0477805f..f0aa08f3a6 100644 --- a/public/less/admin/bootstrap/bootstrap.less +++ b/public/less/admin/bootstrap/bootstrap.less @@ -1,6 +1,6 @@ /*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ diff --git a/public/less/admin/bootstrap/button-groups.less b/public/less/admin/bootstrap/button-groups.less index 293245a650..16db0c6135 100644 --- a/public/less/admin/bootstrap/button-groups.less +++ b/public/less/admin/bootstrap/button-groups.less @@ -59,7 +59,7 @@ .border-right-radius(0); } } -// Need .dropdown-toggle since :last-child doesn't apply given a .dropdown-menu immediately after it +// Need .dropdown-toggle since :last-child doesn't apply, given that a .dropdown-menu is used immediately after it .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { .border-left-radius(0); diff --git a/public/less/admin/bootstrap/forms.less b/public/less/admin/bootstrap/forms.less index e8b071a138..9377d3846b 100644 --- a/public/less/admin/bootstrap/forms.less +++ b/public/less/admin/bootstrap/forms.less @@ -181,7 +181,7 @@ input[type="search"] { // set a pixel line-height that matches the given height of the input, but only // for Safari. See https://bugs.webkit.org/show_bug.cgi?id=139848 // -// Note that as of 8.3, iOS doesn't support `datetime` or `week`. +// Note that as of 9.3, iOS doesn't support `week`. @media screen and (-webkit-min-device-pixel-ratio: 0) { input[type="date"], diff --git a/public/less/admin/bootstrap/input-groups.less b/public/less/admin/bootstrap/input-groups.less index 5f73eec40c..d0763db7ff 100644 --- a/public/less/admin/bootstrap/input-groups.less +++ b/public/less/admin/bootstrap/input-groups.less @@ -29,7 +29,7 @@ width: 100%; margin-bottom: 0; - + &:focus { z-index: 3; } diff --git a/public/less/admin/bootstrap/mixins/tab-focus.less b/public/less/admin/bootstrap/mixins/tab-focus.less index 1f1f05ab05..d12d23629f 100644 --- a/public/less/admin/bootstrap/mixins/tab-focus.less +++ b/public/less/admin/bootstrap/mixins/tab-focus.less @@ -1,9 +1,9 @@ // WebKit-style focus .tab-focus() { - // Default - outline: thin dotted; - // WebKit + // WebKit-specific. Other browsers will keep their default outline style. + // (Initially tried to also force default via `outline: initial`, + // but that seems to erroneously remove the outline in Firefox altogether.) outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } diff --git a/public/less/admin/bootstrap/panels.less b/public/less/admin/bootstrap/panels.less index 425eb5e642..65aa3a83f3 100644 --- a/public/less/admin/bootstrap/panels.less +++ b/public/less/admin/bootstrap/panels.less @@ -214,7 +214,7 @@ } -// Collapsable panels (aka, accordion) +// Collapsible panels (aka, accordion) // // Wrap a series of panels in `.panel-group` to turn them into an accordion with // the help of our collapse JavaScript plugin. diff --git a/public/less/admin/bootstrap/scaffolding.less b/public/less/admin/bootstrap/scaffolding.less index 1929bfc5cf..64a29c6a5e 100644 --- a/public/less/admin/bootstrap/scaffolding.less +++ b/public/less/admin/bootstrap/scaffolding.less @@ -120,7 +120,7 @@ hr { // Only display content to screen readers // -// See: http://a11yproject.com/posts/how-to-hide-content/ +// See: http://a11yproject.com/posts/how-to-hide-content .sr-only { position: absolute; diff --git a/public/less/admin/bootstrap/theme.less b/public/less/admin/bootstrap/theme.less index 8f51d913dc..fb6174427b 100644 --- a/public/less/admin/bootstrap/theme.less +++ b/public/less/admin/bootstrap/theme.less @@ -1,6 +1,6 @@ /*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ diff --git a/public/less/admin/bootstrap/variables.less b/public/less/admin/bootstrap/variables.less index d0cf54f043..03b54980ae 100644 --- a/public/less/admin/bootstrap/variables.less +++ b/public/less/admin/bootstrap/variables.less @@ -1,4 +1,4 @@ -// Paper 3.3.5 +// // Variables // -------------------------------------------------- @@ -9,16 +9,16 @@ @gray-base: #000; @gray-darker: lighten(@gray-base, 13.5%); // #222 -@gray-dark: #212121; -@gray: #666; -@gray-light: #bbb; +@gray-dark: lighten(@gray-base, 20%); // #333 +@gray: lighten(@gray-base, 33.5%); // #555 +@gray-light: lighten(@gray-base, 46.7%); // #777 @gray-lighter: lighten(@gray-base, 93.5%); // #eee -@brand-primary: #2196F3; -@brand-success: #4CAF50; -@brand-info: #9C27B0; -@brand-warning: #ff9800; -@brand-danger: #e51c23; +@brand-primary: darken(#428bca, 6.5%); // #337ab7 +@brand-success: #5cb85c; +@brand-info: #5bc0de; +@brand-warning: #f0ad4e; +@brand-danger: #d9534f; //== Scaffolding @@ -28,7 +28,7 @@ //** Background color for ``. @body-bg: #fff; //** Global text color on ``. -@text-color: @gray; +@text-color: @gray-dark; //** Global textual link color. @link-color: @brand-primary; @@ -42,33 +42,33 @@ // //## Font, line-height, and color for body text, headings, and more. -@font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; +@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; @font-family-serif: Georgia, "Times New Roman", Times, serif; //** Default monospace fonts for ``, ``, and `
`.
 @font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
 @font-family-base:        @font-family-sans-serif;
 
-@font-size-base:          13px;
+@font-size-base:          14px;
 @font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
 @font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
 
-@font-size-h1:            56px;
-@font-size-h2:            45px;
-@font-size-h3:            34px;
-@font-size-h4:            24px;
-@font-size-h5:            20px;
-@font-size-h6:            14px;
+@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
+@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
+@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
+@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
+@font-size-h5:            @font-size-base;
+@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px
 
 //** Unit-less `line-height` for use in components like buttons.
-@line-height-base:        1.846; // 20/14
+@line-height-base:        1.428571429; // 20/14
 //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
 @line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
 
 //** By default, this inherits from the ``.
 @headings-font-family:    inherit;
-@headings-font-weight:    400;
+@headings-font-weight:    500;
 @headings-line-height:    1.1;
-@headings-color:          #444;
+@headings-color:          inherit;
 
 
 //== Iconography
@@ -88,7 +88,7 @@
 //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 
 @padding-base-vertical:     6px;
-@padding-base-horizontal:   16px;
+@padding-base-horizontal:   12px;
 
 @padding-large-vertical:    10px;
 @padding-large-horizontal:  16px;
@@ -102,16 +102,16 @@
 @line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
 @line-height-small:         1.5;
 
-@border-radius-base:        0px;
-@border-radius-large:       0px;
-@border-radius-small:       0px;
+@border-radius-base:        4px;
+@border-radius-large:       6px;
+@border-radius-small:       3px;
 
 //** Global color for active items (e.g., navs or dropdowns).
 @component-active-color:    #fff;
 //** Global background color for active items (e.g., navs or dropdowns).
 @component-active-bg:       @brand-primary;
 
-//** Width of the `border` for generating carets that indicator dropdowns.
+//** Width of the `border` for generating carets that indicate dropdowns.
 @caret-width-base:          4px;
 //** Carets increase slightly in size for larger components.
 @caret-width-large:         5px;
@@ -144,29 +144,29 @@
 
 @btn-font-weight:                normal;
 
-@btn-default-color:              #444;
+@btn-default-color:              #333;
 @btn-default-bg:                 #fff;
-@btn-default-border:             transparent;
+@btn-default-border:             #ccc;
 
 @btn-primary-color:              #fff;
 @btn-primary-bg:                 @brand-primary;
-@btn-primary-border:             transparent;
+@btn-primary-border:             darken(@btn-primary-bg, 5%);
 
 @btn-success-color:              #fff;
 @btn-success-bg:                 @brand-success;
-@btn-success-border:             transparent;
+@btn-success-border:             darken(@btn-success-bg, 5%);
 
 @btn-info-color:                 #fff;
 @btn-info-bg:                    @brand-info;
-@btn-info-border:                transparent;
+@btn-info-border:                darken(@btn-info-bg, 5%);
 
 @btn-warning-color:              #fff;
 @btn-warning-bg:                 @brand-warning;
-@btn-warning-border:             transparent;
+@btn-warning-border:             darken(@btn-warning-bg, 5%);
 
 @btn-danger-color:               #fff;
 @btn-danger-bg:                  @brand-danger;
-@btn-danger-border:              transparent;
+@btn-danger-border:              darken(@btn-danger-bg, 5%);
 
 @btn-link-disabled-color:        @gray-light;
 
@@ -181,14 +181,14 @@
 //##
 
 //** `` background color
-@input-bg:                       transparent;
+@input-bg:                       #fff;
 //** `` background color
-@input-bg-disabled:              transparent;
+@input-bg-disabled:              @gray-lighter;
 
 //** Text color for ``s
 @input-color:                    @gray;
 //** `` border color
-@input-border:                   transparent;
+@input-border:                   #ccc;
 
 // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
 //** Default `.form-control` border radius
@@ -203,7 +203,7 @@
 @input-border-focus:             #66afe9;
 
 //** Placeholder text color
-@input-color-placeholder:        @gray-light;
+@input-color-placeholder:        #999;
 
 //** Default `.form-control` height
 @input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
@@ -219,7 +219,7 @@
 @legend-border-color:            #e5e5e5;
 
 //** Background color for textual input addons
-@input-group-addon-bg:           transparent;
+@input-group-addon-bg:           @gray-lighter;
 //** Border color for textual input addons
 @input-group-addon-border-color: @input-border;
 
@@ -241,11 +241,11 @@
 @dropdown-divider-bg:            #e5e5e5;
 
 //** Dropdown link text color.
-@dropdown-link-color:            @text-color;
+@dropdown-link-color:            @gray-dark;
 //** Hover color for dropdown links.
 @dropdown-link-hover-color:      darken(@gray-dark, 5%);
 //** Hover background for dropdown links.
-@dropdown-link-hover-bg:         @gray-lighter;
+@dropdown-link-hover-bg:         #f5f5f5;
 
 //** Active dropdown menu item text color.
 @dropdown-link-active-color:     @component-active-color;
@@ -259,7 +259,7 @@
 @dropdown-header-color:          @gray-light;
 
 //** Deprecated `@dropdown-caret-color` as of v3.1.0
-@dropdown-caret-color:           @gray-light;
+@dropdown-caret-color:           #000;
 
 
 //-- Z-index master list
@@ -357,45 +357,45 @@
 //##
 
 // Basics of a navbar
-@navbar-height:                    64px;
+@navbar-height:                    50px;
 @navbar-margin-bottom:             @line-height-computed;
 @navbar-border-radius:             @border-radius-base;
 @navbar-padding-horizontal:        floor((@grid-gutter-width / 2));
 @navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
 @navbar-collapse-max-height:       340px;
 
-@navbar-default-color:             @gray-light;
-@navbar-default-bg:                #fff;
-@navbar-default-border:            transparent;
+@navbar-default-color:             #777;
+@navbar-default-bg:                #f8f8f8;
+@navbar-default-border:            darken(@navbar-default-bg, 6.5%);
 
 // Navbar links
-@navbar-default-link-color:                @gray;
-@navbar-default-link-hover-color:          @gray-dark;
+@navbar-default-link-color:                #777;
+@navbar-default-link-hover-color:          #333;
 @navbar-default-link-hover-bg:             transparent;
-@navbar-default-link-active-color:         @gray-dark;
+@navbar-default-link-active-color:         #555;
 @navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);
 @navbar-default-link-disabled-color:       #ccc;
 @navbar-default-link-disabled-bg:          transparent;
 
 // Navbar brand label
 @navbar-default-brand-color:               @navbar-default-link-color;
-@navbar-default-brand-hover-color:         @navbar-default-link-hover-color;
+@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);
 @navbar-default-brand-hover-bg:            transparent;
 
 // Navbar toggle
-@navbar-default-toggle-hover-bg:           transparent;
-@navbar-default-toggle-icon-bar-bg:        rgba(0,0,0,0.5);
-@navbar-default-toggle-border-color:       transparent;
+@navbar-default-toggle-hover-bg:           #ddd;
+@navbar-default-toggle-icon-bar-bg:        #888;
+@navbar-default-toggle-border-color:       #ddd;
 
 
 //=== Inverted navbar
 // Reset inverted navbar basics
-@navbar-inverse-color:                      @gray-light;
-@navbar-inverse-bg:                         @brand-primary;
-@navbar-inverse-border:                     transparent;
+@navbar-inverse-color:                      lighten(@gray-light, 15%);
+@navbar-inverse-bg:                         #222;
+@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
 
 // Inverted navbar links
-@navbar-inverse-link-color:                 lighten(@brand-primary, 30%);
+@navbar-inverse-link-color:                 lighten(@gray-light, 15%);
 @navbar-inverse-link-hover-color:           #fff;
 @navbar-inverse-link-hover-bg:              transparent;
 @navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;
@@ -408,10 +408,10 @@
 @navbar-inverse-brand-hover-color:          #fff;
 @navbar-inverse-brand-hover-bg:             transparent;
 
-// Inverted navbar toggle\
-@navbar-inverse-toggle-hover-bg:            transparent;
-@navbar-inverse-toggle-icon-bar-bg:         rgba(0,0,0,0.5);
-@navbar-inverse-toggle-border-color:        transparent;
+// Inverted navbar toggle
+@navbar-inverse-toggle-hover-bg:            #333;
+@navbar-inverse-toggle-icon-bar-bg:         #fff;
+@navbar-inverse-toggle-border-color:        #333;
 
 
 //== Navs
@@ -426,15 +426,15 @@
 @nav-disabled-link-hover-color:             @gray-light;
 
 //== Tabs
-@nav-tabs-border-color:                     transparent;
+@nav-tabs-border-color:                     #ddd;
 
 @nav-tabs-link-hover-border-color:          @gray-lighter;
 
-@nav-tabs-active-link-hover-bg:             transparent;
+@nav-tabs-active-link-hover-bg:             @body-bg;
 @nav-tabs-active-link-hover-color:          @gray;
-@nav-tabs-active-link-hover-border-color:   transparent;
+@nav-tabs-active-link-hover-border-color:   #ddd;
 
-@nav-tabs-justified-link-border-color:            @nav-tabs-border-color;
+@nav-tabs-justified-link-border-color:            #ddd;
 @nav-tabs-justified-active-link-border-color:     @body-bg;
 
 //== Pills
@@ -486,8 +486,8 @@
 
 @jumbotron-padding:              30px;
 @jumbotron-color:                inherit;
-@jumbotron-bg:                   #f9f9f9;
-@jumbotron-heading-color:        @headings-color;
+@jumbotron-bg:                   @gray-lighter;
+@jumbotron-heading-color:        inherit;
 @jumbotron-font-size:            ceil((@font-size-base * 1.5));
 @jumbotron-heading-font-size:    ceil((@font-size-base * 4.5));
 
@@ -496,20 +496,20 @@
 //
 //## Define colors for form feedback states and, by default, alerts.
 
-@state-success-text:             @brand-success;
+@state-success-text:             #3c763d;
 @state-success-bg:               #dff0d8;
 @state-success-border:           darken(spin(@state-success-bg, -10), 5%);
 
-@state-info-text:                @brand-info;
-@state-info-bg:                  #e1bee7;
+@state-info-text:                #31708f;
+@state-info-bg:                  #d9edf7;
 @state-info-border:              darken(spin(@state-info-bg, -10), 7%);
 
-@state-warning-text:             @brand-warning;
-@state-warning-bg:               #ffe0b2;
+@state-warning-text:             #8a6d3b;
+@state-warning-bg:               #fcf8e3;
 @state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);
 
-@state-danger-text:              @brand-danger;
-@state-danger-bg:                #f9bdbb;
+@state-danger-text:              #a94442;
+@state-danger-bg:                #f2dede;
 @state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);
 
 
@@ -522,7 +522,7 @@
 //** Tooltip text color
 @tooltip-color:               #fff;
 //** Tooltip background color
-@tooltip-bg:                  #727272;
+@tooltip-bg:                  #000;
 @tooltip-opacity:             .9;
 
 //** Tooltip arrow width
@@ -540,9 +540,9 @@
 //** Popover maximum width
 @popover-max-width:                   276px;
 //** Popover border color
-@popover-border-color:                transparent;
+@popover-border-color:                rgba(0,0,0,.2);
 //** Popover fallback border color
-@popover-fallback-border-color:       transparent;
+@popover-fallback-border-color:       #ccc;
 
 //** Popover title background color
 @popover-title-bg:                    darken(@popover-bg, 3%);
@@ -555,7 +555,7 @@
 //** Popover outer arrow width
 @popover-arrow-outer-width:           (@popover-arrow-width + 1);
 //** Popover outer arrow color
-@popover-arrow-outer-color:           fadein(@popover-border-color, 7.5%);
+@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
 //** Popover outer arrow fallback color
 @popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
 
@@ -598,7 +598,7 @@
 //** Background color of modal content area
 @modal-content-bg:                             #fff;
 //** Modal content border color
-@modal-content-border-color:                   transparent;
+@modal-content-border-color:                   rgba(0,0,0,.2);
 //** Modal content border color **for IE8**
 @modal-content-fallback-border-color:          #999;
 
@@ -607,7 +607,7 @@
 //** Modal backdrop opacity
 @modal-backdrop-opacity:      .5;
 //** Modal header border color
-@modal-header-border-color:   transparent;
+@modal-header-border-color:   #e5e5e5;
 //** Modal footer border color
 @modal-footer-border-color:   @modal-header-border-color;
 
@@ -720,21 +720,21 @@
 @panel-primary-border:        @brand-primary;
 @panel-primary-heading-bg:    @brand-primary;
 
-@panel-success-text:          #fff;
+@panel-success-text:          @state-success-text;
 @panel-success-border:        @state-success-border;
-@panel-success-heading-bg:    @brand-success;
+@panel-success-heading-bg:    @state-success-bg;
 
-@panel-info-text:             #fff;
+@panel-info-text:             @state-info-text;
 @panel-info-border:           @state-info-border;
-@panel-info-heading-bg:       @brand-info;
+@panel-info-heading-bg:       @state-info-bg;
 
-@panel-warning-text:          #fff;
+@panel-warning-text:          @state-warning-text;
 @panel-warning-border:        @state-warning-border;
-@panel-warning-heading-bg:    @brand-warning;
+@panel-warning-heading-bg:    @state-warning-bg;
 
-@panel-danger-text:           #fff;
+@panel-danger-text:           @state-danger-text;
 @panel-danger-border:         @state-danger-border;
-@panel-danger-heading-bg:     @brand-danger;
+@panel-danger-heading-bg:     @state-danger-bg;
 
 
 //== Thumbnails
@@ -760,8 +760,8 @@
 //
 //##
 
-@well-bg:                     #f9f9f9;
-@well-border:                 transparent;
+@well-bg:                     #f5f5f5;
+@well-border:                 darken(@well-bg, 7%);
 
 
 //== Badges
@@ -778,7 +778,7 @@
 //** Badge background color in active nav link
 @badge-active-bg:             #fff;
 
-@badge-font-weight:           normal;
+@badge-font-weight:           bold;
 @badge-line-height:           1;
 @badge-border-radius:         10px;
 
@@ -820,9 +820,9 @@
 //
 //##
 
-@close-font-weight:           normal;
+@close-font-weight:           bold;
 @close-color:                 #000;
-@close-text-shadow:           none;
+@close-text-shadow:           0 1px 0 #fff;
 
 
 //== Code
diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js
index 7138bb01b9..1008928bc5 100644
--- a/public/src/admin/manage/users.js
+++ b/public/src/admin/manage/users.js
@@ -231,41 +231,62 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable)
 		});
 
 		function handleUserCreate() {
-			var errorEl = $('#create-modal-error');
 			$('#createUser').on('click', function() {
-				$('#create-modal').modal('show');
-				$('#create-modal form')[0].reset();
-				errorEl.addClass('hide');
+				templates.parse('admin/partials/create_user_modal', {}, function(html) {
+					translator.translate(html, function(html) {
+						bootbox.dialog({
+							message: html,
+							title: 'Create User',
+							onEscape: true,
+							buttons: {
+								cancel: {
+									label: 'Cancel',
+									className: 'btn-link'
+								},
+								create: {
+									label: 'Create',
+									className: 'btn-primary',
+									callback: function(e) {
+										createUser.call(this);
+										return false;
+									}
+								}
+							}
+						});
+					});
+				});
 			});
+		}
 
-			$('#create-modal-go').on('click', function() {
-				var username = $('#create-user-name').val(),
-					email = $('#create-user-email').val(),
-					password = $('#create-user-password').val(),
-					passwordAgain = $('#create-user-password-again').val();
+		function createUser() {
+			var modal = this;
+			var username = document.getElementById('create-user-name').value;
+			var email = document.getElementById('create-user-email').value;
+			var password = document.getElementById('create-user-password').value;
+			var passwordAgain = document.getElementById('create-user-password-again').value;
 
+			var errorEl = $('#create-modal-error');
 
-				if (password !== passwordAgain) {
-					return errorEl.html('Error

Passwords must match!

').removeClass('hide'); + if (password !== passwordAgain) { + return errorEl.html('Error

Passwords must match!

').removeClass('hide'); + } + + var user = { + username: username, + email: email, + password: password + }; + + socket.emit('admin.user.createUser', user, function(err) { + if(err) { + return errorEl.translateHtml('Error

' + err.message + '

').removeClass('hide'); } - var user = { - username: username, - email: email, - password: password - }; - - socket.emit('admin.user.createUser', user, function(err) { - if(err) { - return errorEl.translateHtml('Error

' + err.message + '

').removeClass('hide'); - } - $('#create-modal').modal('hide'); - $('#create-modal').on('hidden.bs.modal', function() { - ajaxify.refresh(); - }); - app.alertSuccess('User created!'); + modal.modal('hide'); + modal.on('hidden.bs.modal', function() { + ajaxify.refresh(); }); - + app.alertSuccess('User created!'); }); } diff --git a/src/meta/css.js b/src/meta/css.js index 4834cedc95..a118d75b1a 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -39,8 +39,7 @@ module.exports = function(Meta) { paths = [ baseThemePath, path.join(__dirname, '../../node_modules'), - path.join(__dirname, '../../public/vendor/fontawesome/less'), - path.join(__dirname, '../../public/vendor/bootstrap/less') + path.join(__dirname, '../../public/vendor/fontawesome/less') ], source = '@import "font-awesome";'; diff --git a/src/views/admin/manage/users.tpl b/src/views/admin/manage/users.tpl index d6dd8c5a25..b2042d8e74 100644 --- a/src/views/admin/manage/users.tpl +++ b/src/views/admin/manage/users.tpl @@ -94,46 +94,6 @@ - - -
diff --git a/src/views/admin/partials/create_user_modal.tpl b/src/views/admin/partials/create_user_modal.tpl new file mode 100644 index 0000000000..b065479b06 --- /dev/null +++ b/src/views/admin/partials/create_user_modal.tpl @@ -0,0 +1,21 @@ +
+
+
+ + +
+
+ + +
+ +
+ + +
+ +
+ + +
+
From c8ba61ac7bc6075f91ecc3a6460f24e5d227a55b Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 00:05:35 +0300 Subject: [PATCH 35/86] closes #4791 --- .gitignore | 1 + src/password.js | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a8ea9cb1eb..a83a93d01f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ npm-debug.log node_modules/ sftp-config.json config.json +jsconfig.json public/src/nodebb.min.js !src/views/config.json public/css/*.css diff --git a/src/password.js b/src/password.js index 13f8c11f72..20f4c79a95 100644 --- a/src/password.js +++ b/src/password.js @@ -12,9 +12,11 @@ }; function forkChild(message, callback) { - var child = fork('./bcrypt', { - silent: true - }); + var forkProcessParams = {}; + if(global.v8debug || parseInt(process.execArgv.indexOf('--debug'), 10) !== -1) { + forkProcessParams = {execArgv: ['--debug=' + (5859), '--nolazy']}; + } + var child = fork('./bcrypt', [], forkProcessParams); child.on('message', function(msg) { if (msg.err) { From 275f5e32e8f15ca28a41ba84d47d054332d0148b Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Thu, 25 Aug 2016 17:22:59 -0400 Subject: [PATCH 36/86] adding back Bootswatch paper variables override --- public/less/admin/bootstrap/variables.less | 180 ++++++++++----------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/public/less/admin/bootstrap/variables.less b/public/less/admin/bootstrap/variables.less index 03b54980ae..eed5b3cfae 100644 --- a/public/less/admin/bootstrap/variables.less +++ b/public/less/admin/bootstrap/variables.less @@ -1,4 +1,4 @@ -// +// Paper 3.3.7 // Variables // -------------------------------------------------- @@ -9,16 +9,16 @@ @gray-base: #000; @gray-darker: lighten(@gray-base, 13.5%); // #222 -@gray-dark: lighten(@gray-base, 20%); // #333 -@gray: lighten(@gray-base, 33.5%); // #555 -@gray-light: lighten(@gray-base, 46.7%); // #777 +@gray-dark: #212121; +@gray: #666; +@gray-light: #bbb; @gray-lighter: lighten(@gray-base, 93.5%); // #eee -@brand-primary: darken(#428bca, 6.5%); // #337ab7 -@brand-success: #5cb85c; -@brand-info: #5bc0de; -@brand-warning: #f0ad4e; -@brand-danger: #d9534f; +@brand-primary: #2196F3; +@brand-success: #4CAF50; +@brand-info: #9C27B0; +@brand-warning: #ff9800; +@brand-danger: #e51c23; //== Scaffolding @@ -28,7 +28,7 @@ //** Background color for ``. @body-bg: #fff; //** Global text color on ``. -@text-color: @gray-dark; +@text-color: @gray; //** Global textual link color. @link-color: @brand-primary; @@ -42,33 +42,33 @@ // //## Font, line-height, and color for body text, headings, and more. -@font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif; +@font-family-sans-serif: "Roboto", "Helvetica Neue", Helvetica, Arial, sans-serif; @font-family-serif: Georgia, "Times New Roman", Times, serif; //** Default monospace fonts for ``, ``, and `
`.
 @font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace;
 @font-family-base:        @font-family-sans-serif;
 
-@font-size-base:          14px;
+@font-size-base:          13px;
 @font-size-large:         ceil((@font-size-base * 1.25)); // ~18px
 @font-size-small:         ceil((@font-size-base * 0.85)); // ~12px
 
-@font-size-h1:            floor((@font-size-base * 2.6)); // ~36px
-@font-size-h2:            floor((@font-size-base * 2.15)); // ~30px
-@font-size-h3:            ceil((@font-size-base * 1.7)); // ~24px
-@font-size-h4:            ceil((@font-size-base * 1.25)); // ~18px
-@font-size-h5:            @font-size-base;
-@font-size-h6:            ceil((@font-size-base * 0.85)); // ~12px
+@font-size-h1:            56px;
+@font-size-h2:            45px;
+@font-size-h3:            34px;
+@font-size-h4:            24px;
+@font-size-h5:            20px;
+@font-size-h6:            14px;
 
 //** Unit-less `line-height` for use in components like buttons.
-@line-height-base:        1.428571429; // 20/14
+@line-height-base:        1.846; // 20/14
 //** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
 @line-height-computed:    floor((@font-size-base * @line-height-base)); // ~20px
 
 //** By default, this inherits from the ``.
 @headings-font-family:    inherit;
-@headings-font-weight:    500;
+@headings-font-weight:    400;
 @headings-line-height:    1.1;
-@headings-color:          inherit;
+@headings-color:          #444;
 
 
 //== Iconography
@@ -88,7 +88,7 @@
 //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 
 @padding-base-vertical:     6px;
-@padding-base-horizontal:   12px;
+@padding-base-horizontal:   16px;
 
 @padding-large-vertical:    10px;
 @padding-large-horizontal:  16px;
@@ -102,8 +102,8 @@
 @line-height-large:         1.3333333; // extra decimals for Win 8.1 Chrome
 @line-height-small:         1.5;
 
-@border-radius-base:        4px;
-@border-radius-large:       6px;
+@border-radius-base:        3px;
+@border-radius-large:       3px;
 @border-radius-small:       3px;
 
 //** Global color for active items (e.g., navs or dropdowns).
@@ -144,29 +144,29 @@
 
 @btn-font-weight:                normal;
 
-@btn-default-color:              #333;
+@btn-default-color:              #444;
 @btn-default-bg:                 #fff;
-@btn-default-border:             #ccc;
+@btn-default-border:             transparent;
 
 @btn-primary-color:              #fff;
 @btn-primary-bg:                 @brand-primary;
-@btn-primary-border:             darken(@btn-primary-bg, 5%);
+@btn-primary-border:             transparent;
 
 @btn-success-color:              #fff;
 @btn-success-bg:                 @brand-success;
-@btn-success-border:             darken(@btn-success-bg, 5%);
+@btn-success-border:             transparent;
 
 @btn-info-color:                 #fff;
 @btn-info-bg:                    @brand-info;
-@btn-info-border:                darken(@btn-info-bg, 5%);
+@btn-info-border:                transparent;
 
 @btn-warning-color:              #fff;
 @btn-warning-bg:                 @brand-warning;
-@btn-warning-border:             darken(@btn-warning-bg, 5%);
+@btn-warning-border:             transparent;
 
 @btn-danger-color:               #fff;
 @btn-danger-bg:                  @brand-danger;
-@btn-danger-border:              darken(@btn-danger-bg, 5%);
+@btn-danger-border:              transparent;
 
 @btn-link-disabled-color:        @gray-light;
 
@@ -181,14 +181,14 @@
 //##
 
 //** `` background color
-@input-bg:                       #fff;
+@input-bg:                       transparent;
 //** `` background color
-@input-bg-disabled:              @gray-lighter;
+@input-bg-disabled:              transparent;
 
 //** Text color for ``s
 @input-color:                    @gray;
 //** `` border color
-@input-border:                   #ccc;
+@input-border:                   transparent;
 
 // TODO: Rename `@input-border-radius` to `@input-border-radius-base` in v4
 //** Default `.form-control` border radius
@@ -203,7 +203,7 @@
 @input-border-focus:             #66afe9;
 
 //** Placeholder text color
-@input-color-placeholder:        #999;
+@input-color-placeholder:        @gray-light;
 
 //** Default `.form-control` height
 @input-height-base:              (@line-height-computed + (@padding-base-vertical * 2) + 2);
@@ -219,7 +219,7 @@
 @legend-border-color:            #e5e5e5;
 
 //** Background color for textual input addons
-@input-group-addon-bg:           @gray-lighter;
+@input-group-addon-bg:           transparent;
 //** Border color for textual input addons
 @input-group-addon-border-color: @input-border;
 
@@ -241,11 +241,11 @@
 @dropdown-divider-bg:            #e5e5e5;
 
 //** Dropdown link text color.
-@dropdown-link-color:            @gray-dark;
+@dropdown-link-color:            @text-color;
 //** Hover color for dropdown links.
 @dropdown-link-hover-color:      darken(@gray-dark, 5%);
 //** Hover background for dropdown links.
-@dropdown-link-hover-bg:         #f5f5f5;
+@dropdown-link-hover-bg:         @gray-lighter;
 
 //** Active dropdown menu item text color.
 @dropdown-link-active-color:     @component-active-color;
@@ -259,7 +259,7 @@
 @dropdown-header-color:          @gray-light;
 
 //** Deprecated `@dropdown-caret-color` as of v3.1.0
-@dropdown-caret-color:           #000;
+@dropdown-caret-color:           @gray-light;
 
 
 //-- Z-index master list
@@ -357,45 +357,45 @@
 //##
 
 // Basics of a navbar
-@navbar-height:                    50px;
+@navbar-height:                    64px;
 @navbar-margin-bottom:             @line-height-computed;
 @navbar-border-radius:             @border-radius-base;
 @navbar-padding-horizontal:        floor((@grid-gutter-width / 2));
 @navbar-padding-vertical:          ((@navbar-height - @line-height-computed) / 2);
 @navbar-collapse-max-height:       340px;
 
-@navbar-default-color:             #777;
-@navbar-default-bg:                #f8f8f8;
-@navbar-default-border:            darken(@navbar-default-bg, 6.5%);
+@navbar-default-color:             @gray-light;
+@navbar-default-bg:                #fff;
+@navbar-default-border:            transparent;
 
 // Navbar links
-@navbar-default-link-color:                #777;
-@navbar-default-link-hover-color:          #333;
+@navbar-default-link-color:                @gray;
+@navbar-default-link-hover-color:          @gray-dark;
 @navbar-default-link-hover-bg:             transparent;
-@navbar-default-link-active-color:         #555;
+@navbar-default-link-active-color:         @gray-dark;
 @navbar-default-link-active-bg:            darken(@navbar-default-bg, 6.5%);
 @navbar-default-link-disabled-color:       #ccc;
 @navbar-default-link-disabled-bg:          transparent;
 
 // Navbar brand label
 @navbar-default-brand-color:               @navbar-default-link-color;
-@navbar-default-brand-hover-color:         darken(@navbar-default-brand-color, 10%);
+@navbar-default-brand-hover-color:         @navbar-default-link-hover-color;
 @navbar-default-brand-hover-bg:            transparent;
 
 // Navbar toggle
-@navbar-default-toggle-hover-bg:           #ddd;
-@navbar-default-toggle-icon-bar-bg:        #888;
-@navbar-default-toggle-border-color:       #ddd;
+@navbar-default-toggle-hover-bg:           transparent;
+@navbar-default-toggle-icon-bar-bg:        rgba(0,0,0,0.5);
+@navbar-default-toggle-border-color:       transparent;
 
 
 //=== Inverted navbar
 // Reset inverted navbar basics
-@navbar-inverse-color:                      lighten(@gray-light, 15%);
-@navbar-inverse-bg:                         #222;
-@navbar-inverse-border:                     darken(@navbar-inverse-bg, 10%);
+@navbar-inverse-color:                      @gray-light;
+@navbar-inverse-bg:                         @brand-primary;
+@navbar-inverse-border:                     transparent;
 
 // Inverted navbar links
-@navbar-inverse-link-color:                 lighten(@gray-light, 15%);
+@navbar-inverse-link-color:                 lighten(@brand-primary, 30%);
 @navbar-inverse-link-hover-color:           #fff;
 @navbar-inverse-link-hover-bg:              transparent;
 @navbar-inverse-link-active-color:          @navbar-inverse-link-hover-color;
@@ -408,10 +408,10 @@
 @navbar-inverse-brand-hover-color:          #fff;
 @navbar-inverse-brand-hover-bg:             transparent;
 
-// Inverted navbar toggle
-@navbar-inverse-toggle-hover-bg:            #333;
-@navbar-inverse-toggle-icon-bar-bg:         #fff;
-@navbar-inverse-toggle-border-color:        #333;
+// Inverted navbar toggle\
+@navbar-inverse-toggle-hover-bg:            transparent;
+@navbar-inverse-toggle-icon-bar-bg:         rgba(0,0,0,0.5);
+@navbar-inverse-toggle-border-color:        transparent;
 
 
 //== Navs
@@ -426,15 +426,15 @@
 @nav-disabled-link-hover-color:             @gray-light;
 
 //== Tabs
-@nav-tabs-border-color:                     #ddd;
+@nav-tabs-border-color:                     transparent;
 
 @nav-tabs-link-hover-border-color:          @gray-lighter;
 
-@nav-tabs-active-link-hover-bg:             @body-bg;
+@nav-tabs-active-link-hover-bg:             transparent;
 @nav-tabs-active-link-hover-color:          @gray;
-@nav-tabs-active-link-hover-border-color:   #ddd;
+@nav-tabs-active-link-hover-border-color:   transparent;
 
-@nav-tabs-justified-link-border-color:            #ddd;
+@nav-tabs-justified-link-border-color:            @nav-tabs-border-color;
 @nav-tabs-justified-active-link-border-color:     @body-bg;
 
 //== Pills
@@ -486,8 +486,8 @@
 
 @jumbotron-padding:              30px;
 @jumbotron-color:                inherit;
-@jumbotron-bg:                   @gray-lighter;
-@jumbotron-heading-color:        inherit;
+@jumbotron-bg:                   #f5f5f5;
+@jumbotron-heading-color:        @headings-color;
 @jumbotron-font-size:            ceil((@font-size-base * 1.5));
 @jumbotron-heading-font-size:    ceil((@font-size-base * 4.5));
 
@@ -496,20 +496,20 @@
 //
 //## Define colors for form feedback states and, by default, alerts.
 
-@state-success-text:             #3c763d;
+@state-success-text:             @brand-success;
 @state-success-bg:               #dff0d8;
 @state-success-border:           darken(spin(@state-success-bg, -10), 5%);
 
-@state-info-text:                #31708f;
-@state-info-bg:                  #d9edf7;
+@state-info-text:                @brand-info;
+@state-info-bg:                  #e1bee7;
 @state-info-border:              darken(spin(@state-info-bg, -10), 7%);
 
-@state-warning-text:             #8a6d3b;
-@state-warning-bg:               #fcf8e3;
+@state-warning-text:             @brand-warning;
+@state-warning-bg:               #ffe0b2;
 @state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);
 
-@state-danger-text:              #a94442;
-@state-danger-bg:                #f2dede;
+@state-danger-text:              @brand-danger;
+@state-danger-bg:                #f9bdbb;
 @state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);
 
 
@@ -522,7 +522,7 @@
 //** Tooltip text color
 @tooltip-color:               #fff;
 //** Tooltip background color
-@tooltip-bg:                  #000;
+@tooltip-bg:                  #727272;
 @tooltip-opacity:             .9;
 
 //** Tooltip arrow width
@@ -540,9 +540,9 @@
 //** Popover maximum width
 @popover-max-width:                   276px;
 //** Popover border color
-@popover-border-color:                rgba(0,0,0,.2);
+@popover-border-color:                transparent;
 //** Popover fallback border color
-@popover-fallback-border-color:       #ccc;
+@popover-fallback-border-color:       transparent;
 
 //** Popover title background color
 @popover-title-bg:                    darken(@popover-bg, 3%);
@@ -555,7 +555,7 @@
 //** Popover outer arrow width
 @popover-arrow-outer-width:           (@popover-arrow-width + 1);
 //** Popover outer arrow color
-@popover-arrow-outer-color:           fadein(@popover-border-color, 5%);
+@popover-arrow-outer-color:           fadein(@popover-border-color, 12%);
 //** Popover outer arrow fallback color
 @popover-arrow-outer-fallback-color:  darken(@popover-fallback-border-color, 20%);
 
@@ -598,7 +598,7 @@
 //** Background color of modal content area
 @modal-content-bg:                             #fff;
 //** Modal content border color
-@modal-content-border-color:                   rgba(0,0,0,.2);
+@modal-content-border-color:                   transparent;
 //** Modal content border color **for IE8**
 @modal-content-fallback-border-color:          #999;
 
@@ -607,7 +607,7 @@
 //** Modal backdrop opacity
 @modal-backdrop-opacity:      .5;
 //** Modal header border color
-@modal-header-border-color:   #e5e5e5;
+@modal-header-border-color:   transparent;
 //** Modal footer border color
 @modal-footer-border-color:   @modal-header-border-color;
 
@@ -720,21 +720,21 @@
 @panel-primary-border:        @brand-primary;
 @panel-primary-heading-bg:    @brand-primary;
 
-@panel-success-text:          @state-success-text;
+@panel-success-text:          #fff;
 @panel-success-border:        @state-success-border;
-@panel-success-heading-bg:    @state-success-bg;
+@panel-success-heading-bg:    @brand-success;
 
-@panel-info-text:             @state-info-text;
+@panel-info-text:             #fff;
 @panel-info-border:           @state-info-border;
-@panel-info-heading-bg:       @state-info-bg;
+@panel-info-heading-bg:       @brand-info;
 
-@panel-warning-text:          @state-warning-text;
+@panel-warning-text:          #fff;
 @panel-warning-border:        @state-warning-border;
-@panel-warning-heading-bg:    @state-warning-bg;
+@panel-warning-heading-bg:    @brand-warning;
 
-@panel-danger-text:           @state-danger-text;
+@panel-danger-text:           #fff;
 @panel-danger-border:         @state-danger-border;
-@panel-danger-heading-bg:     @state-danger-bg;
+@panel-danger-heading-bg:     @brand-danger;
 
 
 //== Thumbnails
@@ -761,7 +761,7 @@
 //##
 
 @well-bg:                     #f5f5f5;
-@well-border:                 darken(@well-bg, 7%);
+@well-border:                 transparent;
 
 
 //== Badges
@@ -778,7 +778,7 @@
 //** Badge background color in active nav link
 @badge-active-bg:             #fff;
 
-@badge-font-weight:           bold;
+@badge-font-weight:           normal;
 @badge-line-height:           1;
 @badge-border-radius:         10px;
 
@@ -820,9 +820,9 @@
 //
 //##
 
-@close-font-weight:           bold;
+@close-font-weight:           normal;
 @close-color:                 #000;
-@close-text-shadow:           0 1px 0 #fff;
+@close-text-shadow:           none;
 
 
 //== Code
@@ -866,4 +866,4 @@
 //** Point at which .dl-horizontal becomes horizontal
 @dl-horizontal-breakpoint:    @grid-float-breakpoint;
 //** Horizontal line color.
-@hr-border:                   @gray-lighter;
+@hr-border:                   @gray-lighter;
\ No newline at end of file

From 1339c4155df0bbbc67065377f487cbf6c65e272d Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 11:19:12 +0300
Subject: [PATCH 37/86] up composer

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index b498b13924..97c0d9c6bb 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
     "morgan": "^1.3.2",
     "mousetrap": "^1.5.3",
     "nconf": "~0.8.2",
-    "nodebb-plugin-composer-default": "4.1.9",
+    "nodebb-plugin-composer-default": "4.1.10",
     "nodebb-plugin-dbsearch": "1.0.2",
     "nodebb-plugin-emoji-extended": "1.1.1",
     "nodebb-plugin-emoji-one": "1.1.5",

From 97f0d2438f31ebbd2bbaa837ed30066586b95341 Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 12:33:44 +0300
Subject: [PATCH 38/86] up timeout to 500ms

---
 src/controllers/admin/info.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/controllers/admin/info.js b/src/controllers/admin/info.js
index 2459ad7140..00a5694937 100644
--- a/src/controllers/admin/info.js
+++ b/src/controllers/admin/info.js
@@ -25,7 +25,7 @@ infoController.get = function(req, res, next) {
 			return (a.os.hostname < b.os.hostname) ? -1 : (a.os.hostname > b.os.hostname) ? 1 : 0;
 		});
 		res.render('admin/development/info', {info: data, infoJSON: JSON.stringify(data, null, 4), host: os.hostname(), port: nconf.get('port')});
-	}, 300);
+	}, 500);
 };
 
 pubsub.on('sync:node:info:start', function() {

From e5ecb333f9304bdac21a26319d0d0415ad400db6 Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 12:53:00 +0300
Subject: [PATCH 39/86] closes #4978

---
 public/src/require-config.js            | 11 +++++++++++
 src/meta/js.js                          |  1 +
 src/views/admin/header.tpl              | 17 ++---------------
 src/views/partials/requirejs-config.tpl | 13 -------------
 4 files changed, 14 insertions(+), 28 deletions(-)
 create mode 100644 public/src/require-config.js
 delete mode 100644 src/views/partials/requirejs-config.tpl

diff --git a/public/src/require-config.js b/public/src/require-config.js
new file mode 100644
index 0000000000..0ad2f8a58c
--- /dev/null
+++ b/public/src/require-config.js
@@ -0,0 +1,11 @@
+require.config({
+	baseUrl: config.relative_path + "/src/modules",
+	waitSeconds: 7,
+	urlArgs: "v=" + config['cache-buster'],
+	paths: {
+		'forum': '../client',
+		'admin': '../admin',
+		'vendor': '../../vendor',
+		'plugins': '../../plugins'
+	}
+});
diff --git a/src/meta/js.js b/src/meta/js.js
index b781a3b06f..ffd66f010e 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -26,6 +26,7 @@ module.exports = function(Meta) {
 				'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js',
 				'public/vendor/jquery/textcomplete/jquery.textcomplete.js',
 				'public/vendor/requirejs/require.js',
+				'public/src/require-config.js',
 				'public/vendor/bootbox/bootbox.min.js',
 				'public/vendor/tinycon/tinycon.js',
 				'public/vendor/xregexp/xregexp.js',
diff --git a/src/views/admin/header.tpl b/src/views/admin/header.tpl
index 3d797ed5be..b5a0f196c3 100644
--- a/src/views/admin/header.tpl
+++ b/src/views/admin/header.tpl
@@ -16,7 +16,8 @@
 			template: "{template.name}",
 			user: JSON.parse('{{userJSON}}'),
 			config: JSON.parse(decodeURIComponent("{{adminConfigJSON}}")),
-			flags: {}
+			flags: {},
+			inAdmin: true
 		};
 	
 
@@ -30,20 +31,6 @@
 		
 		
 		
-		
 		
 		
 		
diff --git a/src/views/partials/requirejs-config.tpl b/src/views/partials/requirejs-config.tpl
deleted file mode 100644
index c11b4aa9a3..0000000000
--- a/src/views/partials/requirejs-config.tpl
+++ /dev/null
@@ -1,13 +0,0 @@
-
\ No newline at end of file

From de34a98636548cf51f76802240af5cce7062f284 Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 13:08:29 +0300
Subject: [PATCH 40/86] up themes

---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index 97c0d9c6bb..97c7ca45fa 100644
--- a/package.json
+++ b/package.json
@@ -57,8 +57,8 @@
     "nodebb-plugin-spam-be-gone": "0.4.10",
     "nodebb-rewards-essentials": "0.0.9",
     "nodebb-theme-lavender": "3.0.13",
-    "nodebb-theme-persona": "4.1.33",
-    "nodebb-theme-vanilla": "5.1.18",
+    "nodebb-theme-persona": "4.1.34",
+    "nodebb-theme-vanilla": "5.1.19",
     "nodebb-widget-essentials": "2.0.10",
     "nodemailer": "2.0.0",
     "nodemailer-sendmail-transport": "1.0.0",

From b7fbc7a5319c2e5ab4670b53465d8cd3a93a9b19 Mon Sep 17 00:00:00 2001
From: NodeBB Misty 
Date: Fri, 26 Aug 2016 09:03:14 -0400
Subject: [PATCH 41/86] Latest translations and fallbacks

---
 public/language/ar/global.json        | 4 +++-
 public/language/bg/global.json        | 2 ++
 public/language/bn/global.json        | 4 +++-
 public/language/cs/global.json        | 4 +++-
 public/language/da/global.json        | 4 +++-
 public/language/de/global.json        | 4 +++-
 public/language/el/global.json        | 4 +++-
 public/language/en@pirate/global.json | 4 +++-
 public/language/en_US/global.json     | 4 +++-
 public/language/es/global.json        | 4 +++-
 public/language/et/global.json        | 4 +++-
 public/language/fa_IR/global.json     | 4 +++-
 public/language/fi/global.json        | 4 +++-
 public/language/fr/global.json        | 4 +++-
 public/language/gl/global.json        | 4 +++-
 public/language/he/global.json        | 4 +++-
 public/language/hu/global.json        | 4 +++-
 public/language/id/global.json        | 4 +++-
 public/language/it/global.json        | 4 +++-
 public/language/ja/global.json        | 4 +++-
 public/language/ko/global.json        | 4 +++-
 public/language/lt/global.json        | 4 +++-
 public/language/ms/global.json        | 4 +++-
 public/language/nb/global.json        | 4 +++-
 public/language/nl/global.json        | 4 +++-
 public/language/pl/global.json        | 4 +++-
 public/language/pt_BR/global.json     | 4 +++-
 public/language/ro/global.json        | 4 +++-
 public/language/ru/global.json        | 4 +++-
 public/language/rw/global.json        | 4 +++-
 public/language/sc/global.json        | 4 +++-
 public/language/sk/global.json        | 4 +++-
 public/language/sl/global.json        | 4 +++-
 public/language/sr/global.json        | 4 +++-
 public/language/sv/global.json        | 4 +++-
 public/language/th/global.json        | 4 +++-
 public/language/tr/global.json        | 4 +++-
 public/language/vi/global.json        | 4 +++-
 public/language/zh_CN/error.json      | 8 ++++----
 public/language/zh_CN/global.json     | 6 ++++--
 public/language/zh_CN/login.json      | 2 +-
 public/language/zh_CN/modules.json    | 2 +-
 public/language/zh_CN/topic.json      | 6 +++---
 public/language/zh_TW/global.json     | 4 +++-
 44 files changed, 129 insertions(+), 49 deletions(-)

diff --git a/public/language/ar/global.json b/public/language/ar/global.json
index 96356a3bce..2e4d39d113 100644
--- a/public/language/ar/global.json
+++ b/public/language/ar/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "لم يتم العثور",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "خطأ داخلي.",
+    "500.title": "Internal Error.",
     "500.message": "عفوا! يبدو وكأنه شيء ذهب على نحو خاطئ!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "تسجيل",
     "login": "دخول",
     "please_log_in": "المرجو تسجيل الدخول",
diff --git a/public/language/bg/global.json b/public/language/bg/global.json
index 189c8d3990..6a68bfcec3 100644
--- a/public/language/bg/global.json
+++ b/public/language/bg/global.json
@@ -9,6 +9,8 @@
     "404.message": "Изглежда сте се опитали да посетите страница, която не съществува. Върнете се към началната страница.",
     "500.title": "Вътрешна грешка.",
     "500.message": "Опа! Изглежда нещо се обърка!",
+    "400.title": "Грешна заявка.",
+    "400.message": "Тази връзка изглежда повредена. Моля, проверете я и опитайте отново. В противен случай се върнете на началната страница.",
     "register": "Регистрация",
     "login": "Вход",
     "please_log_in": "Моля, влезте",
diff --git a/public/language/bn/global.json b/public/language/bn/global.json
index bfde1d9003..f678961fcd 100644
--- a/public/language/bn/global.json
+++ b/public/language/bn/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "পাওয়া যায়নি",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "অভ্যন্তরীণ ত্রুটি।",
+    "500.title": "Internal Error.",
     "500.message": "ওহো! কিছু ভুল হয়েছে মনে হচ্ছে!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "নিবন্ধন",
     "login": "প্রবেশ",
     "please_log_in": "অনুগ্রহ করে প্রবেশ করুন",
diff --git a/public/language/cs/global.json b/public/language/cs/global.json
index d78d3797f4..80a7ee4825 100644
--- a/public/language/cs/global.json
+++ b/public/language/cs/global.json
@@ -7,8 +7,10 @@
     "403.login": "Možná byste měli se zkusit přihlásit?",
     "404.title": "Stránka nenalezena",
     "404.message": "Zdá se, že jste narazil/a na stránku která neexistuje. Vrátit se zpět na domovskou stránku.",
-    "500.title": "Neznámá chyba",
+    "500.title": "Internal Error.",
     "500.message": "Jejda, vypadá to, že se něco pokazilo.",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrovat",
     "login": "Přihlásit se",
     "please_log_in": "Přihlašte se, prosím",
diff --git a/public/language/da/global.json b/public/language/da/global.json
index 2eda261502..5f6d693e1f 100644
--- a/public/language/da/global.json
+++ b/public/language/da/global.json
@@ -7,8 +7,10 @@
     "403.login": "Måske du skulle prøve og logge ind?",
     "404.title": "Ikke fundet",
     "404.message": "Det ser ud til du er stødt på en side der ikke finder. Retuner til  forsiden.",
-    "500.title": "Intern fejl.",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Ser ud til at noget gik galt!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Tilmeld",
     "login": "Log ind",
     "please_log_in": "Venligst log ind",
diff --git a/public/language/de/global.json b/public/language/de/global.json
index 496c212943..775e81cbfa 100644
--- a/public/language/de/global.json
+++ b/public/language/de/global.json
@@ -7,8 +7,10 @@
     "403.login": "Du solltest Dich anmelden.",
     "404.title": " Nicht Gefunden",
     "404.message": "Diese Seite existiert nicht. Zur Homepage zurückkehren.",
-    "500.title": "Interner Fehler.",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Scheint als wäre etwas schief gelaufen!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrieren",
     "login": "Anmelden",
     "please_log_in": "Bitte anmelden",
diff --git a/public/language/el/global.json b/public/language/el/global.json
index 32eacb6466..1c475bdf03 100644
--- a/public/language/el/global.json
+++ b/public/language/el/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "Δεν βρέθηκε",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Εσωτερικό σφάλμα.",
+    "500.title": "Internal Error.",
     "500.message": "Ουπς! Φαίνεται πως κάτι πήγε στραβά!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Εγγραφή",
     "login": "Σύνδεση",
     "please_log_in": "Παρακαλώ Συνδέσου",
diff --git a/public/language/en@pirate/global.json b/public/language/en@pirate/global.json
index 6c86c89999..9bb8beecd3 100644
--- a/public/language/en@pirate/global.json
+++ b/public/language/en@pirate/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "T'ere be nut'in 'ere",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Broken beam.",
+    "500.title": "Internal Error.",
     "500.message": "Looks like we've got somethin' in th' sails.",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Register",
     "login": "Login",
     "please_log_in": "Please Log In",
diff --git a/public/language/en_US/global.json b/public/language/en_US/global.json
index e7f511d90a..004929c8a8 100644
--- a/public/language/en_US/global.json
+++ b/public/language/en_US/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "Not Found",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Internal error.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Looks like something went wrong!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Register",
     "login": "Login",
     "please_log_in": "Please Log In",
diff --git a/public/language/es/global.json b/public/language/es/global.json
index 3cf4afbbae..d75547742a 100644
--- a/public/language/es/global.json
+++ b/public/language/es/global.json
@@ -7,8 +7,10 @@
     "403.login": "¿Quizás deberías  intentar acceder?",
     "404.title": "No encontrado",
     "404.message": "Al parecer has llegado a una página a la cual no tienes permisos para acceder. Volver a la  página de inicio .",
-    "500.title": "Error Interno.",
+    "500.title": "Error interno.",
     "500.message": "¡Ooops! ¡Parece que algo salió mal! No te preocupes, ¡nuestros simios hiperinteligentes lo solucionarán!",
+    "400.title": "Petición incorrecta.",
+    "400.message": "Parece que la dirección es errónea, por favor compruébala y prueba otra vez. En caso contrario vuelve al inicio.",
     "register": "Registrarse",
     "login": "Conectarse",
     "please_log_in": "Por favor, identifíquese.",
diff --git a/public/language/et/global.json b/public/language/et/global.json
index 3add6a941c..ba6bb6c304 100644
--- a/public/language/et/global.json
+++ b/public/language/et/global.json
@@ -7,8 +7,10 @@
     "403.login": "Äkki peaksid sisse logima?",
     "404.title": "Ei leitud",
     "404.message": "Tundub, et lehte mida otsid, ei eksisteeri. Mine tagasi avalehele.",
-    "500.title": "Süsteemi viga",
+    "500.title": "Internal Error.",
     "500.message": "Oih! Midagi läks valesti!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registreeri",
     "login": "Logi sisse",
     "please_log_in": "Palun logi sisse",
diff --git a/public/language/fa_IR/global.json b/public/language/fa_IR/global.json
index ce48647e40..a8da7a5cd6 100644
--- a/public/language/fa_IR/global.json
+++ b/public/language/fa_IR/global.json
@@ -7,8 +7,10 @@
     "403.login": "شاید باید وارد شوید؟",
     "404.title": "یافت نشد",
     "404.message": "به نظر میاید شما به صفحه ای برخورد کرده اید که وجود ندارد. بازگشت به صفحه ی خانه",
-    "500.title": "خطای درونی.",
+    "500.title": "Internal Error.",
     "500.message": "اوه! گویا اشتباهی رخ داده!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "نام‌نویسی",
     "login": "درون آمدن",
     "please_log_in": "لطفا به درون بیایید",
diff --git a/public/language/fi/global.json b/public/language/fi/global.json
index 8488b07bfe..a2fd93afde 100644
--- a/public/language/fi/global.json
+++ b/public/language/fi/global.json
@@ -7,8 +7,10 @@
     "403.login": "Sinun pitäisi kai kirjautua sisään?",
     "404.title": "Ei löydy",
     "404.message": "Olet päätynyt sivulle, jota ei ole olemassa. Palaa etusivulle.",
-    "500.title": "Sisäinen virhe.",
+    "500.title": "Internal Error.",
     "500.message": "Oho! Jotain meni pieleen!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Rekisteröidy",
     "login": "Kirjaudu",
     "please_log_in": "Kirjaudu, ole hyvä",
diff --git a/public/language/fr/global.json b/public/language/fr/global.json
index 10b70a2148..ba4388b9fe 100644
--- a/public/language/fr/global.json
+++ b/public/language/fr/global.json
@@ -7,8 +7,10 @@
     "403.login": "Peut-être deviez vous  essayer de vous connecter?",
     "404.title": "Introuvable",
     "404.message": "Il semble que vous ayez atteint une page qui n'existe pas. Retourner à la page d'accueil.",
-    "500.title": "Erreur interne.",
+    "500.title": "Erreur Interne.",
     "500.message": "Oops ! Il semblerait que quelque chose se soit mal passé !",
+    "400.title": "Requête erronée.",
+    "400.message": "Il semble que ce lien ne soit pas correct, merci de le vérifier. Sinon, retournez à la page d'accueil.",
     "register": "S'inscrire",
     "login": "Se connecter",
     "please_log_in": "Veuillez vous connecter",
diff --git a/public/language/gl/global.json b/public/language/gl/global.json
index 8d1301e078..c00aec98d1 100644
--- a/public/language/gl/global.json
+++ b/public/language/gl/global.json
@@ -7,8 +7,10 @@
     "403.login": "Quizais deberías tentar iniciar sesión?",
     "404.title": "Non Atopado",
     "404.message": "Ao parecer, esta páxina non existe. Volver ao Inicio.",
-    "500.title": "Erro interno.",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Parece que algo saíu mal!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Rexistrarse",
     "login": "Conectarse",
     "please_log_in": "Por favor, conéctate",
diff --git a/public/language/he/global.json b/public/language/he/global.json
index 6e76c19ea3..ea6d32468d 100644
--- a/public/language/he/global.json
+++ b/public/language/he/global.json
@@ -7,8 +7,10 @@
     "403.login": "נסה להתחבר.",
     "404.title": "לא נמצא",
     "404.message": "נראה שהגעת לעמוד שלא קיים. חזור לעמוד הבית",
-    "500.title": "שגיאה פנימית.",
+    "500.title": "Internal Error.",
     "500.message": "אופס! נראה שמשהו השתבש!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "הרשמה",
     "login": "התחברות",
     "please_log_in": "אנא התחבר",
diff --git a/public/language/hu/global.json b/public/language/hu/global.json
index b7f0edef28..e7111389bc 100644
--- a/public/language/hu/global.json
+++ b/public/language/hu/global.json
@@ -7,8 +7,10 @@
     "403.login": "Talán meg kellene próbálnod belépni?",
     "404.title": "Nincs találat",
     "404.message": "Úgy tűnik, hogy rábukkantál egy olyan oldalra ami nem létezik. Visszatérés a kezdőlapra",
-    "500.title": "Belső hiba.",
+    "500.title": "Internal Error.",
     "500.message": "Hoppá! Úgy tűnik valami hiba történt!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Regisztráció",
     "login": "Belépés",
     "please_log_in": "Jelentkezzünk be",
diff --git a/public/language/id/global.json b/public/language/id/global.json
index aac14e74d7..6a005844c4 100644
--- a/public/language/id/global.json
+++ b/public/language/id/global.json
@@ -7,8 +7,10 @@
     "403.login": "Mungkin kamu harus mencoba untuk login?",
     "404.title": "Tidak ditemukan",
     "404.message": "Kamu kelihatan mengakses halaman yang tidak ada. Kembali ke beranda.",
-    "500.title": "Kesalahan internal",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Terjadi kesalahan",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Daftar",
     "login": "Login",
     "please_log_in": "Silakan Log In",
diff --git a/public/language/it/global.json b/public/language/it/global.json
index 0bebb5cd9e..a9b0e569b7 100644
--- a/public/language/it/global.json
+++ b/public/language/it/global.json
@@ -7,8 +7,10 @@
     "403.login": "Forse dovresti effettuare l'accesso?",
     "404.title": "Non trovato",
     "404.message": "Sembra tu sia arrivato ad una pagina che non esiste. Torna alla home page.",
-    "500.title": "Errore interno.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Qualcosa non funziona come si deve!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrazione",
     "login": "Login",
     "please_log_in": "Per favore Accedi",
diff --git a/public/language/ja/global.json b/public/language/ja/global.json
index 1e89ac1a32..038f6333f0 100644
--- a/public/language/ja/global.json
+++ b/public/language/ja/global.json
@@ -7,8 +7,10 @@
     "403.login": "権限を持っている場合はログインすると閲覧出来ます。",
     "404.title": "見つかりません",
     "404.message": "あなたは存在してないページを訪問してます。ホームページに戻ります。",
-    "500.title": "内部エラー",
+    "500.title": "Internal Error.",
     "500.message": "何か問題が発生しているようです。",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "登録",
     "login": "ログイン",
     "please_log_in": "ログインください",
diff --git a/public/language/ko/global.json b/public/language/ko/global.json
index e404ac910a..94905f6ffc 100644
--- a/public/language/ko/global.json
+++ b/public/language/ko/global.json
@@ -7,8 +7,10 @@
     "403.login": "로그인되어 있는지 확인해 주세요.",
     "404.title": "페이지를 찾을 수 없습니다.",
     "404.message": "존재하지 않는 페이지에 접근하였습니다. 홈 페이지로 이동합니다.",
-    "500.title": "내부 오류가 발생했습니다.",
+    "500.title": "Internal Error.",
     "500.message": "알 수 없는 오류가 발생했습니다.",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "회원가입",
     "login": "로그인",
     "please_log_in": "로그인해 주세요.",
diff --git a/public/language/lt/global.json b/public/language/lt/global.json
index 3892b4ede8..6f5399905d 100644
--- a/public/language/lt/global.json
+++ b/public/language/lt/global.json
@@ -7,8 +7,10 @@
     "403.login": "Tikriausiai tu turėtum pabandyt prisijungt?",
     "404.title": "Nerasta",
     "404.message": "Pasirodo sėdi puslapyje kurio net nėra. Grįžk į namų puslapį.",
-    "500.title": "Vidinė klaida.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Atrodo, kad kažkas nutiko!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registruotis",
     "login": "Prisijungti",
     "please_log_in": "Prašome prisijungti",
diff --git a/public/language/ms/global.json b/public/language/ms/global.json
index e226d5f93c..dc65efdc18 100644
--- a/public/language/ms/global.json
+++ b/public/language/ms/global.json
@@ -7,8 +7,10 @@
     "403.login": "Mungkin anda boleh cuba log masuk?",
     "404.title": "tidak dijumpai",
     "404.message": "Halaman yang diminta tidak wujud. Kembali ke halaman utama.",
-    "500.title": "ralat dalaman",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Macam ada yang tidak kena",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Daftar",
     "login": "Log Masuk",
     "please_log_in": "Sila log masuk",
diff --git a/public/language/nb/global.json b/public/language/nb/global.json
index 1e8af5dc12..10d96d584e 100644
--- a/public/language/nb/global.json
+++ b/public/language/nb/global.json
@@ -7,8 +7,10 @@
     "403.login": "Kanskje du skal prøve å logge inn?",
     "404.title": "Ikke funnet",
     "404.message": "Du har funnet en side som ikke eksisterer. Returner til startsiden?",
-    "500.title": "Intern feil.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Ser ut som noe gikk galt!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrer",
     "login": "Logg inn",
     "please_log_in": "Vennligst logg inn",
diff --git a/public/language/nl/global.json b/public/language/nl/global.json
index c2cf89003a..59c43d4893 100644
--- a/public/language/nl/global.json
+++ b/public/language/nl/global.json
@@ -7,8 +7,10 @@
     "403.login": "Je kan proberen in te loggen?",
     "404.title": "Niet gevonden",
     "404.message": "Deze pagina bestaat niet. Klik hier om naar de hoofdpagina van deze website te navigeren.",
-    "500.title": "Interne fout.",
+    "500.title": "Internal Error.",
     "500.message": "Oeps! Ziet er naar uit dat iets fout ging!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registeren",
     "login": "Login",
     "please_log_in": "Aanmelden",
diff --git a/public/language/pl/global.json b/public/language/pl/global.json
index f3a25ee6b2..4054a256fe 100644
--- a/public/language/pl/global.json
+++ b/public/language/pl/global.json
@@ -7,8 +7,10 @@
     "403.login": "Może powinieneś się zalogować?",
     "404.title": "Nie znaleziono",
     "404.message": "Wygląda na to, że trafiłeś na stronę, która nie istnieje. Wróć do strony głównej.",
-    "500.title": "Błąd wewnętrzny",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Coś poszło nie tak.",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Zarejestruj się",
     "login": "Zaloguj się",
     "please_log_in": "Proszę się zalogować",
diff --git a/public/language/pt_BR/global.json b/public/language/pt_BR/global.json
index 69c907a516..27e5782a3d 100644
--- a/public/language/pt_BR/global.json
+++ b/public/language/pt_BR/global.json
@@ -7,8 +7,10 @@
     "403.login": "Talvez você deveria tentar fazer login?",
     "404.title": "Não Encontrado",
     "404.message": "Parece que você chegou à uma página que não existe. Voltar para a página inicial.",
-    "500.title": "Erro interno.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Parece que algo deu errado!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Cadastrar",
     "login": "Login",
     "please_log_in": "Por Favor Efetue o Login",
diff --git a/public/language/ro/global.json b/public/language/ro/global.json
index 7d2f15e71c..7a843384a0 100644
--- a/public/language/ro/global.json
+++ b/public/language/ro/global.json
@@ -7,8 +7,10 @@
     "403.login": "Poate ar trebui să te autentifici?",
     "404.title": "Nu a fost găsit",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Eroare internă.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Se pare că ceva a mers greșit!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Înregistrare",
     "login": "Autentificare",
     "please_log_in": "Autentifică-te",
diff --git a/public/language/ru/global.json b/public/language/ru/global.json
index d5ed6dc2b9..2e453f412e 100644
--- a/public/language/ru/global.json
+++ b/public/language/ru/global.json
@@ -7,8 +7,10 @@
     "403.login": "Возможно Вам следует войти под своим аккаунтом?",
     "404.title": "Страница не найдена",
     "404.message": "Вы пытаетесь перейти на страницу, которой не существует. Вам стоит вернутся на главную страницу.",
-    "500.title": "Внутренняя ошибка.",
+    "500.title": "Internal Error.",
     "500.message": "Упс! Похоже, что-то пошло не так!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Зарегистрироваться",
     "login": "Войти",
     "please_log_in": "Пожалуйста, войдите под своим аккаунтом",
diff --git a/public/language/rw/global.json b/public/language/rw/global.json
index 1f82d7cdfd..088182851c 100644
--- a/public/language/rw/global.json
+++ b/public/language/rw/global.json
@@ -7,8 +7,10 @@
     "403.login": "Wenda ahari ukeneye kugerageza kwinjiramo",
     "404.title": "Ntacyabonetse",
     "404.message": "Biragaragara ko wageze kuri paji itariho ikintu. Subira Imbere.",
-    "500.title": "Hari ikibazo cya tekinike imbere. ",
+    "500.title": "Internal Error.",
     "500.message": "Ye baba we! Ntibikunze!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Iyandikishe",
     "login": "Injiramo",
     "please_log_in": "Injiramo",
diff --git a/public/language/sc/global.json b/public/language/sc/global.json
index 9f7f952155..7c70438c6c 100644
--- a/public/language/sc/global.json
+++ b/public/language/sc/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "No Agatadu",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Faddina interna.",
+    "500.title": "Internal Error.",
     "500.message": "Oops! Paret chi carchi cosa est andada male!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registra·ti",
     "login": "Intra",
     "please_log_in": "Pro praghere Intra",
diff --git a/public/language/sk/global.json b/public/language/sk/global.json
index 14cc91fb07..f292d3f667 100644
--- a/public/language/sk/global.json
+++ b/public/language/sk/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "Stránka nenájdená",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "Neznámá chyba",
+    "500.title": "Internal Error.",
     "500.message": "Jejda, vyzerá, že sa niečo pokazilo.",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrovať",
     "login": "Prihlásiť sa",
     "please_log_in": "Prosím, prihláste sa",
diff --git a/public/language/sl/global.json b/public/language/sl/global.json
index 911fb6a7ff..993d32a131 100644
--- a/public/language/sl/global.json
+++ b/public/language/sl/global.json
@@ -7,8 +7,10 @@
     "403.login": "Morda bi se raje  prijavili?",
     "404.title": "Ni mogoče najti",
     "404.message": "Kot kaže ste naleteli na stran, ki ne obstaja. Vrnite se na začetno stran.",
-    "500.title": "Notranja napaka.",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Nekaj je šlo narobe!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registracija",
     "login": "Prijava",
     "please_log_in": "Prosimo prijavite se",
diff --git a/public/language/sr/global.json b/public/language/sr/global.json
index 97e71581ea..6cee270049 100644
--- a/public/language/sr/global.json
+++ b/public/language/sr/global.json
@@ -7,8 +7,10 @@
     "403.login": "Можда би требало да се пријавите?",
     "404.title": "Не постоји",
     "404.message": "Изгледа да сте наишли на страницу која не постоји. Вратите се на почетну страницу..",
-    "500.title": "Унутрашња грешка.",
+    "500.title": "Internal Error.",
     "500.message": "Упс! Изгледа да нешто ије како треба!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Регистрација",
     "login": "Пријава",
     "please_log_in": "Молимо, пријавите се",
diff --git a/public/language/sv/global.json b/public/language/sv/global.json
index 5c1b98522e..847c19a5c0 100644
--- a/public/language/sv/global.json
+++ b/public/language/sv/global.json
@@ -7,8 +7,10 @@
     "403.login": "Du kanske bör försöka logga in?",
     "404.title": "Sidan saknas",
     "404.message": "Du verkar ha ramlat in på en sida som inte finns. Återgå till första sidan.",
-    "500.title": "Internt fel.",
+    "500.title": "Internal Error.",
     "500.message": "Hoppsan! Något verkar ha gått fel!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Registrera",
     "login": "Logga in",
     "please_log_in": "Var god logga in",
diff --git a/public/language/th/global.json b/public/language/th/global.json
index eb81fb4ba5..4e4b7cccb7 100644
--- a/public/language/th/global.json
+++ b/public/language/th/global.json
@@ -7,8 +7,10 @@
     "403.login": "Perhaps you should try logging in?",
     "404.title": "ไม่พบ",
     "404.message": "You seem to have stumbled upon a page that does not exist. Return to the home page.",
-    "500.title": "มีข้อผิดพลาดภายในระบบ",
+    "500.title": "Internal Error.",
     "500.message": "อุ่ย! มีสิ่งที่ไม่ถูกต้องเกิดขึ้น!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "ลงทะเบียน",
     "login": "เข้าสู่ระบบ",
     "please_log_in": "กรุณาเข้าสู่ระบบ",
diff --git a/public/language/tr/global.json b/public/language/tr/global.json
index 9e3c328dae..54801fc108 100644
--- a/public/language/tr/global.json
+++ b/public/language/tr/global.json
@@ -7,8 +7,10 @@
     "403.login": "Belki de tekrar giriş yapmayı denersiniz?",
     "404.title": "Bulunamadı",
     "404.message": "Erişim izniniz olmayan bir sayfaya denk gelmiş gibisiniz. Anasayfa'ya geri dönün.",
-    "500.title": "Dahili hata.",
+    "500.title": "Internal Error.",
     "500.message": "Ups! Bir şeyler ters gitti sanki!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Kayıt Ol",
     "login": "Giriş",
     "please_log_in": "Lütfen Giriş Yapınız",
diff --git a/public/language/vi/global.json b/public/language/vi/global.json
index 2cbb1536c8..1043051526 100644
--- a/public/language/vi/global.json
+++ b/public/language/vi/global.json
@@ -7,8 +7,10 @@
     "403.login": "Có lẽ bạn nên thử đăng nhập?",
     "404.title": "Không tìm thấy",
     "404.message": "Có vẻ như bạn đang cố vào một trang không tồn tại. Hãy trở lại trang chủ.",
-    "500.title": "Lỗi nội bộ",
+    "500.title": "Internal Error.",
     "500.message": "Úi chà! Có vẻ như có trục trặc rồi!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "Đăng ký",
     "login": "Đăng nhập",
     "please_log_in": "Xin hãy đăng nhập",
diff --git a/public/language/zh_CN/error.json b/public/language/zh_CN/error.json
index 5788c57c59..e3d3aa55d4 100644
--- a/public/language/zh_CN/error.json
+++ b/public/language/zh_CN/error.json
@@ -20,7 +20,7 @@
     "email-taken": "此电子邮箱已被占用",
     "email-not-confirmed": "您的电子邮箱尚未确认,请点击这里确认您的电子邮箱。",
     "email-not-confirmed-chat": "您的电子邮箱尚未确认,无法聊天,请点击这里确认您的电子邮箱。",
-    "email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email.",
+    "email-not-confirmed-email-sent": "您的邮箱地址还没有被确认,请检查邮箱中的确认邮件。",
     "no-email-to-confirm": "本论坛需要电子邮箱确认,请点击这里输入电子邮箱地址",
     "email-confirm-failed": "我们无法确认您的电子邮箱,请重试",
     "confirm-email-already-sent": "确认邮件已发出,如需重新发送请等待 %1 分钟后再试。",
@@ -55,8 +55,8 @@
     "post-delete-duration-expired-hours-minutes": "您只能在发表 %1 小时 %2 分钟后删除帖子",
     "post-delete-duration-expired-days": "您只能在发表 %1 天后删除帖子",
     "post-delete-duration-expired-days-hours": "您只能在发表 %1 天 %2 小时后删除帖子",
-    "cant-delete-topic-has-reply": "You can't delete your topic after it has a reply",
-    "cant-delete-topic-has-replies": "You can't delete your topic after it has %1 replies",
+    "cant-delete-topic-has-reply": "您不能删除您的主题,因为已有回复。",
+    "cant-delete-topic-has-replies": "您不能删除您的主题,因为已有 %1 条回复。",
     "content-too-short": "请增添发帖内容,不能少于 %1 个字符。",
     "content-too-long": "请删减发帖内容,不能超过 %1 个字符。",
     "title-too-short": "请增加标题,不能少于 %1 个字符。",
@@ -123,5 +123,5 @@
     "no-users-in-room": "这个聊天室中没有用户",
     "cant-kick-self": "你不能把自己踢出群组",
     "no-users-selected": "尚未选择用户",
-    "invalid-home-page-route": "Invalid home page route"
+    "invalid-home-page-route": "无效的首页路径"
 }
\ No newline at end of file
diff --git a/public/language/zh_CN/global.json b/public/language/zh_CN/global.json
index 4be989cb9f..9d6a0d5a47 100644
--- a/public/language/zh_CN/global.json
+++ b/public/language/zh_CN/global.json
@@ -7,8 +7,10 @@
     "403.login": "或许您应该先 登录试试?",
     "404.title": "未找到",
     "404.message": "您访问的页面不存在。返回首页。",
-    "500.title": "内部错误。",
+    "500.title": "内部错误",
     "500.message": "哎呀!看来是哪里出错了!",
+    "400.title": "错误的请求",
+    "400.message": "看起来这个链接的格式不正确,请再次检查并重试。或者返回主页。",
     "register": "注册",
     "login": "登录",
     "please_log_in": "请登录",
@@ -93,5 +95,5 @@
     "upload_file": "上传文件",
     "upload": "上传",
     "allowed-file-types": "允许的文件类型有 %1",
-    "unsaved-changes": "You have unsaved changes. Are you sure you wish to navigate away?"
+    "unsaved-changes": "您有未保存的更改,您确定您要离开么?"
 }
\ No newline at end of file
diff --git a/public/language/zh_CN/login.json b/public/language/zh_CN/login.json
index d9d5e10980..d46196e47d 100644
--- a/public/language/zh_CN/login.json
+++ b/public/language/zh_CN/login.json
@@ -8,5 +8,5 @@
     "failed_login_attempt": "登录失败",
     "login_successful": "您已经成功登录!",
     "dont_have_account": "没有帐号?",
-    "logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity"
+    "logged-out-due-to-inactivity": "由于长时间不活动,您已从控制面板注销"
 }
\ No newline at end of file
diff --git a/public/language/zh_CN/modules.json b/public/language/zh_CN/modules.json
index 869a8bf0c0..b00e1c9aa4 100644
--- a/public/language/zh_CN/modules.json
+++ b/public/language/zh_CN/modules.json
@@ -37,7 +37,7 @@
     "composer.formatting.picture": "图片",
     "composer.upload-picture": "上传图片",
     "composer.upload-file": "上传文件",
-    "composer.zen_mode": "Zen Mode",
+    "composer.zen_mode": "无干扰模式",
     "bootbox.ok": "确认",
     "bootbox.cancel": "取消",
     "bootbox.confirm": "确认",
diff --git a/public/language/zh_CN/topic.json b/public/language/zh_CN/topic.json
index 072da54e1a..0d4168ffdc 100644
--- a/public/language/zh_CN/topic.json
+++ b/public/language/zh_CN/topic.json
@@ -26,8 +26,8 @@
     "tools": "工具",
     "flag": "举报",
     "locked": "已锁定",
-    "pinned": "Pinned",
-    "moved": "Moved",
+    "pinned": "已固定",
+    "moved": "已移动",
     "bookmark_instructions": "点击阅读本主题帖中的最新回复",
     "flag_title": "举报此帖",
     "flag_success": "已举报此回帖。",
@@ -86,7 +86,7 @@
     "topic_will_be_moved_to": "此主题将被移动到版块",
     "fork_topic_instruction": "点击将分割的帖子",
     "fork_no_pids": "未选中帖子!",
-    "fork_pid_count": "%1 post(s) selected",
+    "fork_pid_count": "选择了 %1 个帖子",
     "fork_success": "成功分割主题! 点这里跳转到分割后的主题。",
     "delete_posts_instruction": "点击想要删除/永久删除的帖子",
     "composer.title_placeholder": "在此输入您主题的标题...",
diff --git a/public/language/zh_TW/global.json b/public/language/zh_TW/global.json
index d530c0ace1..f36d9ed09e 100644
--- a/public/language/zh_TW/global.json
+++ b/public/language/zh_TW/global.json
@@ -7,8 +7,10 @@
     "403.login": "或許是你應該 試著登入?",
     "404.title": "無法找到該頁",
     "404.message": "你所查找的頁面並不存在。返回首頁。",
-    "500.title": "內部錯誤",
+    "500.title": "Internal Error.",
     "500.message": "糟糕! 看來是不知道哪裡出錯了!",
+    "400.title": "Bad Request.",
+    "400.message": "It looks like this link is malformed, please double-check and try again. Otherwise, return to the home page.",
     "register": "註冊",
     "login": "登入",
     "please_log_in": "請先登入",

From 5c830758061bd333e2dd9105c4a18816916e55ef Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 16:39:03 +0300
Subject: [PATCH 42/86] moved variables parse to ajaxify.js

---
 public/src/ajaxify.js   | 10 ++++++++--
 public/src/app.js       |  1 +
 public/src/variables.js | 15 ---------------
 src/meta/js.js          |  1 -
 4 files changed, 9 insertions(+), 18 deletions(-)
 delete mode 100644 public/src/variables.js

diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js
index 8237a9961b..87122bf78c 100644
--- a/public/src/ajaxify.js
+++ b/public/src/ajaxify.js
@@ -198,8 +198,6 @@ $(document).ready(function() {
 		}
 		var count = 2;
 
-		ajaxify.variables.parse();
-
 		ajaxify.loadScript(tpl_url, done);
 
 		ajaxify.widgets.render(tpl_url, url, done);
@@ -209,6 +207,14 @@ $(document).ready(function() {
 		app.processPage();
 	};
 
+	ajaxify.parseData = function() {
+		var dataEl = $('#ajaxify-data');
+		if (dataEl.length) {
+			ajaxify.data = JSON.parse(dataEl.text());
+			dataEl.remove();
+		}
+	};
+
 	ajaxify.removeRelativePath = function(url) {
 		if (url.startsWith(RELATIVE_PATH.slice(1))) {
 			url = url.slice(RELATIVE_PATH.length);
diff --git a/public/src/app.js b/public/src/app.js
index e66d0d03c2..b1896e071a 100644
--- a/public/src/app.js
+++ b/public/src/app.js
@@ -24,6 +24,7 @@ app.cacheBuster = null;
 
 		var url = ajaxify.start(window.location.pathname.slice(1) + window.location.search + window.location.hash);
 		ajaxify.updateHistory(url, true);
+		ajaxify.parseData();
 		ajaxify.end(url, app.template);
 
 		handleStatusChange();
diff --git a/public/src/variables.js b/public/src/variables.js
deleted file mode 100644
index 487099ac3c..0000000000
--- a/public/src/variables.js
+++ /dev/null
@@ -1,15 +0,0 @@
-"use strict";
-/*global ajaxify*/
-
-(function(ajaxify) {
-
-	ajaxify.variables = {};
-
-	ajaxify.variables.parse = function() {
-		var dataEl = $('#ajaxify-data');
-		if (dataEl.length) {
-			ajaxify.data = JSON.parse(dataEl.text());
-			dataEl.remove();
-		}
-	};
-}(ajaxify || {}));
diff --git a/src/meta/js.js b/src/meta/js.js
index ffd66f010e..848ee16e6f 100644
--- a/src/meta/js.js
+++ b/src/meta/js.js
@@ -37,7 +37,6 @@ module.exports = function(Meta) {
 				'public/src/app.js',
 				'public/src/ajaxify.js',
 				'public/src/overrides.js',
-				'public/src/variables.js',
 				'public/src/widgets.js'
 			],
 

From 3c1a4876d72724506da31993557c2fce714614b6 Mon Sep 17 00:00:00 2001
From: barisusakli 
Date: Fri, 26 Aug 2016 17:01:11 +0300
Subject: [PATCH 43/86] removed unused vars

---
 public/src/client/infinitescroll.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/public/src/client/infinitescroll.js b/public/src/client/infinitescroll.js
index 42443aa27e..ed21602b01 100644
--- a/public/src/client/infinitescroll.js
+++ b/public/src/client/infinitescroll.js
@@ -1,8 +1,8 @@
 'use strict';
 
-/* globals define, socket, ajaxify, templates, app */
+/* globals define, socket, app */
 
-define('forum/infinitescroll', ['translator'], function(translator) {
+define('forum/infinitescroll', function() {
 
 	var scroll = {};
 	var callback;

From 91980089071c5c603dd3494007059d45d12af22f Mon Sep 17 00:00:00 2001
From: Julian Lam 
Date: Fri, 26 Aug 2016 09:20:44 -0400
Subject: [PATCH 44/86] forcing ACP settingsv1 to wait a beat before firing
 client-side hook so client-side scripts can register listeners first

---
 public/src/admin/settings.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js
index 9e616cfe80..bf6a0b4130 100644
--- a/public/src/admin/settings.js
+++ b/public/src/admin/settings.js
@@ -109,7 +109,9 @@ define('admin/settings', ['uploader'], function(uploader) {
 			callback();
 		}
 
-		$(window).trigger('action:admin.settingsLoaded');
+		setTimeout(function() {
+			$(window).trigger('action:admin.settingsLoaded');
+		}, 0);
 	};
 
 	function handleUploads() {

From e9770b6bd5b75eae1444ac32c58e8a419a1898cb Mon Sep 17 00:00:00 2001
From: Julian Lam 
Date: Fri, 26 Aug 2016 09:39:13 -0400
Subject: [PATCH 45/86] firing client-side hook when settingsv1 saves

---
 public/src/admin/settings.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/public/src/admin/settings.js b/public/src/admin/settings.js
index bf6a0b4130..d6436ccefc 100644
--- a/public/src/admin/settings.js
+++ b/public/src/admin/settings.js
@@ -93,6 +93,8 @@ define('admin/settings', ['uploader'], function(uploader) {
 					message: 'Your changes to the NodeBB configuration have been saved.',
 					type: 'success'
 				});
+
+				$(window).trigger('action:admin.settingsSaved');
 			});
 		});
 

From 25cd772e0efcf027ac373c0a30261a634d251bd8 Mon Sep 17 00:00:00 2001
From: Julian Lam 
Date: Fri, 26 Aug 2016 10:04:38 -0400
Subject: [PATCH 46/86] closes #4766

---
 public/src/admin/settings/email.js | 30 ++++++++++++++
 src/socket.io/admin/user.js        |  3 ++
 src/socket.io/meta.js              |  5 ++-
 src/user/jobs.js                   | 64 ++++++++++++++++++++++++------
 src/views/admin/settings/email.tpl | 15 ++++++-
 5 files changed, 102 insertions(+), 15 deletions(-)

diff --git a/public/src/admin/settings/email.js b/public/src/admin/settings/email.js
index e6015b3b32..78a058e19d 100644
--- a/public/src/admin/settings/email.js
+++ b/public/src/admin/settings/email.js
@@ -8,6 +8,11 @@ define('admin/settings/email', ['admin/settings'], function(settings) {
 	module.init = function() {
 		configureEmailTester();
 		configureEmailEditor();
+
+		$(window).on('action:admin.settingsLoaded action:admin.settingsSaved', handleDigestHourChange);
+		$(window).on('action:admin.settingsSaved', function() {
+			socket.emit('admin.user.restartJobs');
+		});
 	};
 
 	function configureEmailTester() {
@@ -57,5 +62,30 @@ define('admin/settings/email', ['admin/settings'], function(settings) {
 		});
 	}
 
+	function handleDigestHourChange() {
+		var hour = parseInt($('#digestHour').val(), 10);
+
+		if (isNaN(hour)) {
+			hour = 17;
+		} else if (hour > 23 || hour < 0) {
+			hour = 0;
+		}
+
+		socket.emit('meta.getServerTime', {}, function(err, now) {
+			now = new Date(now);
+
+			$('#serverTime').text(now.toString());
+
+			now.setHours(parseInt(hour, 10), 0, 0, 0);
+
+			// If adjusted time is in the past, move to next day
+			if (now.getTime() < Date.now()) {
+				now.setDate(now.getDate() + 1);
+			}
+
+			$('#nextDigestTime').text(now.toString());
+		});
+	}
+
 	return module;
 });
diff --git a/src/socket.io/admin/user.js b/src/socket.io/admin/user.js
index 3fd69a8d10..42dcd23266 100644
--- a/src/socket.io/admin/user.js
+++ b/src/socket.io/admin/user.js
@@ -226,5 +226,8 @@ User.rejectRegistration = function(socket, data, callback) {
 	user.rejectRegistration(data.username, callback);
 };
 
+User.restartJobs = function(socket, data, callback) {
+	user.startJobs(callback);
+};
 
 module.exports = User;
diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js
index 39603c8d0c..e5c114f372 100644
--- a/src/socket.io/meta.js
+++ b/src/socket.io/meta.js
@@ -68,6 +68,9 @@ function leaveCurrentRoom(socket) {
 	}
 }
 
-
+SocketMeta.getServerTime = function(socket, data, callback) {
+	// Returns server time in milliseconds
+	callback(null, Date.now());
+};
 
 module.exports = SocketMeta;
diff --git a/src/user/jobs.js b/src/user/jobs.js
index 16f6bd4ce8..9c9569dc40 100644
--- a/src/user/jobs.js
+++ b/src/user/jobs.js
@@ -1,29 +1,69 @@
-
 'use strict';
 
-var winston = require('winston'),
-	cronJob = require('cron').CronJob,
+var winston = require('winston');
+var cronJob = require('cron').CronJob;
 
-	meta = require('../meta');
+var meta = require('../meta');
+
+var jobs = {};
 
 module.exports = function(User) {
-	User.startJobs = function() {
-		new cronJob('0 0 17 * * *', function() {
-			winston.verbose('[user.startJobs] Digest job (daily) started.');
+	User.startJobs = function(callback) {
+		winston.verbose('[user/jobs] (Re-)starting user jobs...');
+		var terminated = 0;
+		var started = 0;
+		var digestHour = parseInt(meta.config.digestHour, 10);
+
+		// Fix digest hour if invalid
+		if (isNaN(digestHour)) {
+			digestHour = 17;
+		} else if (digestHour > 23 || digestHour < 0) {
+			digestHour = 0;
+		}
+
+		// Terminate any active cron jobs
+		for(var jobId in jobs) {
+			if (jobs.hasOwnProperty(jobId)) {
+				winston.verbose('[user/jobs] Terminating job (' + jobId + ')');
+				jobs[jobId].stop();
+				delete jobs[jobId];
+				++terminated;
+			}
+		}
+		winston.verbose('[user/jobs] ' + terminated + ' jobs terminated');
+
+		jobs['digest.daily'] = new cronJob('0 0 ' + digestHour + ' * * *', function() {
+			winston.verbose('[user/jobs] Digest job (daily) started.');
 			User.digest.execute('day');
 		}, null, true);
+		winston.verbose('[user/jobs] Starting job (digest.daily)');
+		++started;
 
-		new cronJob('0 0 17 * * 0', function() {
-			winston.verbose('[user.startJobs] Digest job (weekly) started.');
+		jobs['digest.weekly'] = new cronJob('0 0 ' + digestHour + ' * * 0', function() {
+			winston.verbose('[user/jobs] Digest job (weekly) started.');
 			User.digest.execute('week');
 		}, null, true);
+		winston.verbose('[user/jobs] Starting job (digest.weekly)');
+		++started;
 
-		new cronJob('0 0 17 1 * *', function() {
-			winston.verbose('[user.startJobs] Digest job (monthly) started.');
+		jobs['digest.monthly'] = new cronJob('0 0 ' + digestHour + ' 1 * *', function() {
+			winston.verbose('[user/jobs] Digest job (monthly) started.');
 			User.digest.execute('month');
 		}, null, true);
+		winston.verbose('[user/jobs] Starting job (digest.monthly)');
+		++started;
 
-		new cronJob('0 0 0 * * *', User.reset.clean, null, true);
+		jobs['reset.clean'] = new cronJob('0 0 0 * * *', User.reset.clean, null, true);
+		winston.verbose('[user/jobs] Starting job (reset.clean)');
+		++started;
+
+		winston.verbose('[user/jobs] ' + started + ' jobs started');
+
+		if (typeof callback === 'function') {
+			callback();
+		}
+
+		return;
 	};
 };
 
diff --git a/src/views/admin/settings/email.tpl b/src/views/admin/settings/email.tpl
index cb49aa17b5..3b65cbc0a4 100644
--- a/src/views/admin/settings/email.tpl
+++ b/src/views/admin/settings/email.tpl
@@ -34,14 +34,14 @@
 			
 			
-
+

Enter the full email address here, especially if you are using a Google Apps managed domain.

-
+
@@ -92,6 +92,17 @@ Disable subscriber notification emails + +
+ + +

+ Please enter a number representing the hour to send scheduled email digests (e.g. 0 for midnight, 17 for 5:00pm). + Keep in mind that this is the hour according to the server itself, and may not exactly match your system clock.
+ The approximate server time is:
+ The next daily digest is scheduled to be sent +

+
From f5941cbf1c8b542bfe8a0c10fd969a5e9139cc5d Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 17:14:08 +0300 Subject: [PATCH 47/86] removed unused requires --- src/meta.js | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/meta.js b/src/meta.js index 8dfbc0d99a..2aca05f308 100644 --- a/src/meta.js +++ b/src/meta.js @@ -1,18 +1,14 @@ "use strict"; -var async = require('async'), - winston = require('winston'), - templates = require('templates.js'), - os = require('os'), - nconf = require('nconf'), +var async = require('async'); +var winston = require('winston'); - user = require('./user'), - groups = require('./groups'), - languages = require('./languages'), - emitter = require('./emitter'), - pubsub = require('./pubsub'), - auth = require('./routes/authentication'), - utils = require('../public/src/utils'); +var os = require('os'); +var nconf = require('nconf'); +var user = require('./user'); +var groups = require('./groups'); +var pubsub = require('./pubsub'); +var utils = require('../public/src/utils'); (function (Meta) { Meta.reloadRequired = false; From 8f408faf4649ff87a4e6491e2473fbfa5f2efe16 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 18:50:37 +0300 Subject: [PATCH 48/86] organize middlewares removed app.locals.middleware middlewares can be required anywhere, ie in controllers --- src/controllers/index.js | 10 +- src/meta/configs.js | 11 +- src/middleware/admin.js | 205 +++++++++---------- src/middleware/header.js | 6 +- src/middleware/headers.js | 51 +++++ src/middleware/index.js | 259 ++++++++++++++++------- src/middleware/middleware.js | 384 ----------------------------------- src/middleware/user.js | 154 ++++++++++++++ src/plugins.js | 4 +- src/webserver.js | 109 ++++++++-- 10 files changed, 602 insertions(+), 591 deletions(-) create mode 100644 src/middleware/headers.js delete mode 100644 src/middleware/middleware.js create mode 100644 src/middleware/user.js diff --git a/src/controllers/index.js b/src/controllers/index.js index 975dca54ae..d39352e6d0 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -376,8 +376,8 @@ Controllers.handle404 = function(req, res) { if (res.locals.isAPI) { return res.json({path: validator.escape(path.replace(/^\/api/, '')), title: '[[global:404.title]]'}); } - - req.app.locals.middleware.buildHeader(req, res, function() { + var middleware = require('../middleware'); + middleware.buildHeader(req, res, function() { res.render('404', {path: validator.escape(path), title: '[[global:404.title]]'}); }); } else { @@ -402,7 +402,8 @@ Controllers.handleURIErrors = function(err, req, res, next) { error: '[[global:400.title]]' }); } else { - req.app.locals.middleware.buildHeader(req, res, function() { + var middleware = require('../middleware'); + middleware.buildHeader(req, res, function() { res.render('400', { error: validator.escape(String(err.message)) }); }); } @@ -435,7 +436,8 @@ Controllers.handleErrors = function(err, req, res, next) { if (res.locals.isAPI) { res.json({path: validator.escape(path), error: err.message}); } else { - req.app.locals.middleware.buildHeader(req, res, function() { + var middleware = require('../middleware'); + middleware.buildHeader(req, res, function() { res.render('500', { path: validator.escape(path), error: validator.escape(String(err.message)) }); }); } diff --git a/src/meta/configs.js b/src/meta/configs.js index 69cc375b85..61464c232a 100644 --- a/src/meta/configs.js +++ b/src/meta/configs.js @@ -1,11 +1,12 @@ 'use strict'; -var winston = require('winston'), - db = require('../database'), - pubsub = require('../pubsub'), - nconf = require('nconf'), - utils = require('../../public/src/utils'); +var winston = require('winston'); +var nconf = require('nconf'); + +var db = require('../database'); +var pubsub = require('../pubsub'); +var utils = require('../../public/src/utils'); module.exports = function(Meta) { diff --git a/src/middleware/admin.js b/src/middleware/admin.js index 2ee0f7fd80..5e4399f226 100644 --- a/src/middleware/admin.js +++ b/src/middleware/admin.js @@ -1,130 +1,125 @@ "use strict"; -var app, - middleware = {}, - nconf = require('nconf'), - async = require('async'), - winston = require('winston'), - user = require('../user'), - meta = require('../meta'), - plugins = require('../plugins'), +var async = require('async'); +var winston = require('winston'); +var user = require('../user'); +var meta = require('../meta'); +var plugins = require('../plugins'); - controllers = { - api: require('../controllers/api'), - helpers: require('../controllers/helpers') - }; - -middleware.isAdmin = function(req, res, next) { - winston.warn('[middleware.admin.isAdmin] deprecation warning, no need to use this from plugins!'); - - if (!req.user) { - return controllers.helpers.notAllowed(req, res); - } - - user.isAdministrator(req.user.uid, function (err, isAdmin) { - if (err || isAdmin) { - return next(err); - } - - controllers.helpers.notAllowed(req, res); - }); +var controllers = { + api: require('../controllers/api'), + helpers: require('../controllers/helpers') }; -middleware.buildHeader = function(req, res, next) { - res.locals.renderAdminHeader = true; +module.exports = function(middleware) { + middleware.admin = {}; + middleware.admin.isAdmin = function(req, res, next) { + winston.warn('[middleware.admin.isAdmin] deprecation warning, no need to use this from plugins!'); - async.parallel({ - config: function(next) { - controllers.api.getConfig(req, res, next); - }, - footer: function(next) { - app.render('admin/footer', {}, next); - } - }, function(err, results) { - if (err) { - return next(err); + if (!req.user) { + return controllers.helpers.notAllowed(req, res); } - res.locals.config = results.config; - res.locals.adminFooter = results.footer; - next(); - }); -}; + user.isAdministrator(req.user.uid, function (err, isAdmin) { + if (err || isAdmin) { + return next(err); + } -middleware.renderHeader = function(req, res, data, next) { - var custom_header = { - 'plugins': [], - 'authentication': [] + controllers.helpers.notAllowed(req, res); + }); }; - user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) { - if (err) { - return next(err); - } - - userData.uid = req.uid; - userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1; + middleware.admin.buildHeader = function(req, res, next) { + res.locals.renderAdminHeader = true; async.parallel({ - scripts: function(next) { - plugins.fireHook('filter:admin.scripts.get', [], function(err, scripts) { - if (err) { - return next(err); - } - var arr = []; - scripts.forEach(function(script) { - arr.push({src: script}); - }); - - next(null, arr); - }); - }, - custom_header: function(next) { - plugins.fireHook('filter:admin.header.build', custom_header, next); - }, config: function(next) { controllers.api.getConfig(req, res, next); }, - configs: function(next) { - meta.configs.list(next); + footer: function(next) { + req.app.render('admin/footer', {}, next); } }, function(err, results) { if (err) { return next(err); } + res.locals.config = results.config; - - var acpPath = req.path.slice(1).split('/'); - acpPath.forEach(function(path, i) { - acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1); - }); - acpPath = acpPath.join(' > '); - - var templateValues = { - config: results.config, - configJSON: JSON.stringify(results.config), - relative_path: results.config.relative_path, - adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)), - user: userData, - userJSON: JSON.stringify(userData).replace(/'/g, "\\'"), - plugins: results.custom_header.plugins, - authentication: results.custom_header.authentication, - scripts: results.scripts, - 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '', - env: process.env.NODE_ENV ? true : false, - title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel', - bodyClass: data.bodyClass - }; - - templateValues.template = {name: res.locals.template}; - templateValues.template[res.locals.template] = true; - - app.render('admin/header', templateValues, next); + res.locals.adminFooter = results.footer; + next(); }); - }); -}; + }; -module.exports = function(webserver) { - app = webserver; - return middleware; + middleware.admin.renderHeader = function(req, res, data, next) { + var custom_header = { + 'plugins': [], + 'authentication': [] + }; + + user.getUserFields(req.uid, ['username', 'userslug', 'email', 'picture', 'email:confirmed'], function(err, userData) { + if (err) { + return next(err); + } + + userData.uid = req.uid; + userData['email:confirmed'] = parseInt(userData['email:confirmed'], 10) === 1; + + async.parallel({ + scripts: function(next) { + plugins.fireHook('filter:admin.scripts.get', [], function(err, scripts) { + if (err) { + return next(err); + } + var arr = []; + scripts.forEach(function(script) { + arr.push({src: script}); + }); + + next(null, arr); + }); + }, + custom_header: function(next) { + plugins.fireHook('filter:admin.header.build', custom_header, next); + }, + config: function(next) { + controllers.api.getConfig(req, res, next); + }, + configs: function(next) { + meta.configs.list(next); + } + }, function(err, results) { + if (err) { + return next(err); + } + res.locals.config = results.config; + + var acpPath = req.path.slice(1).split('/'); + acpPath.forEach(function(path, i) { + acpPath[i] = path.charAt(0).toUpperCase() + path.slice(1); + }); + acpPath = acpPath.join(' > '); + + var templateValues = { + config: results.config, + configJSON: JSON.stringify(results.config), + relative_path: results.config.relative_path, + adminConfigJSON: encodeURIComponent(JSON.stringify(results.configs)), + user: userData, + userJSON: JSON.stringify(userData).replace(/'/g, "\\'"), + plugins: results.custom_header.plugins, + authentication: results.custom_header.authentication, + scripts: results.scripts, + 'cache-buster': meta.config['cache-buster'] ? 'v=' + meta.config['cache-buster'] : '', + env: process.env.NODE_ENV ? true : false, + title: (acpPath || 'Dashboard') + ' | NodeBB Admin Control Panel', + bodyClass: data.bodyClass + }; + + templateValues.template = {name: res.locals.template}; + templateValues.template[res.locals.template] = true; + + req.app.render('admin/header', templateValues, next); + }); + }); + }; }; diff --git a/src/middleware/header.js b/src/middleware/header.js index 6425c17d74..b4013d1481 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -16,7 +16,7 @@ var controllers = { helpers: require('../controllers/helpers') }; -module.exports = function(app, middleware) { +module.exports = function(middleware) { middleware.buildHeader = function(req, res, next) { res.locals.renderHeader = true; @@ -28,7 +28,7 @@ module.exports = function(app, middleware) { controllers.api.getConfig(req, res, next); }, footer: function(next) { - app.render('footer', { + req.app.render('footer', { loggedIn: !!req.uid, title: validator.escape(meta.config.title || meta.config.browserTitle || 'NodeBB') }, next); @@ -160,7 +160,7 @@ module.exports = function(app, middleware) { return callback(err); } - app.render('header', data.templateValues, callback); + req.app.render('header', data.templateValues, callback); }); }); }; diff --git a/src/middleware/headers.js b/src/middleware/headers.js new file mode 100644 index 0000000000..39c9520e59 --- /dev/null +++ b/src/middleware/headers.js @@ -0,0 +1,51 @@ +'use strict'; + + +var meta = require('../meta'); +var _ = require('underscore'); + + +module.exports = function(middleware) { + + middleware.addHeaders = function (req, res, next) { + var defaults = { + 'X-Powered-By': 'NodeBB', + 'X-Frame-Options': 'SAMEORIGIN', + 'Access-Control-Allow-Origin': 'null' // yes, string null. + }; + var headers = { + 'X-Powered-By': meta.config['powered-by'], + 'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + meta.config['allow-from-uri'] : undefined, + 'Access-Control-Allow-Origin': meta.config['access-control-allow-origin'], + 'Access-Control-Allow-Methods': meta.config['access-control-allow-methods'], + 'Access-Control-Allow-Headers': meta.config['access-control-allow-headers'] + }; + + _.defaults(headers, defaults); + headers = _.pick(headers, Boolean); // Remove falsy headers + + for(var key in headers) { + if (headers.hasOwnProperty(key)) { + res.setHeader(key, headers[key]); + } + } + + next(); + }; + + middleware.addExpiresHeaders = function(req, res, next) { + if (req.app.enabled('cache')) { + res.setHeader("Cache-Control", "public, max-age=5184000"); + res.setHeader("Expires", new Date(Date.now() + 5184000000).toUTCString()); + } else { + res.setHeader("Cache-Control", "public, max-age=0"); + res.setHeader("Expires", new Date().toUTCString()); + } + + next(); + }; + +}; + + + diff --git a/src/middleware/index.js b/src/middleware/index.js index 867af6fed2..266f2b1488 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -1,83 +1,202 @@ "use strict"; -var meta = require('../meta'), - db = require('../database'), - file = require('../file'), - auth = require('../routes/authentication'), +var async = require('async'); +var fs = require('fs'); +var path = require('path'); +var csrf = require('csurf'); +var validator = require('validator'); +var nconf = require('nconf'); +var ensureLoggedIn = require('connect-ensure-login'); +var toobusy = require('toobusy-js'); - path = require('path'), - nconf = require('nconf'), - flash = require('connect-flash'), - templates = require('templates.js'), - bodyParser = require('body-parser'), - cookieParser = require('cookie-parser'), - compression = require('compression'), - favicon = require('serve-favicon'), - session = require('express-session'), - useragent = require('express-useragent'); +var plugins = require('../plugins'); +var languages = require('../languages'); +var meta = require('../meta'); +var user = require('../user'); +var groups = require('../groups'); +var analytics = require('../analytics'); + +var controllers = { + api: require('./../controllers/api'), + helpers: require('../controllers/helpers') +}; var middleware = {}; -function setupFavicon(app) { - var faviconPath = path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico'); - if (file.existsSync(faviconPath)) { - app.use(nconf.get('relative_path'), favicon(faviconPath)); +middleware.applyCSRF = csrf(); + +middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login'); + +require('./admin')(middleware); +require('./header')(middleware); +require('./render')(middleware); +require('./maintenance')(middleware); +require('./user')(middleware); +require('./headers')(middleware); + +middleware.authenticate = function(req, res, next) { + if (req.user) { + return next(); + } else if (plugins.hasListeners('action:middleware.authenticate')) { + return plugins.fireHook('action:middleware.authenticate', { + req: req, + res: res, + next: next + }); } + + controllers.helpers.notAllowed(req, res); +}; + +middleware.pageView = function(req, res, next) { + analytics.pageView({ + ip: req.ip, + path: req.path, + uid: req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') ? parseInt(req.user.uid, 10) : 0 + }); + + plugins.fireHook('action:middleware.pageView', {req: req}); + + if (req.user) { + user.updateLastOnlineTime(req.user.uid); + if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) { + user.updateOnlineUsers(req.user.uid, next); + } else { + user.updateOnlineUsers(req.user.uid); + next(); + } + } else { + next(); + } +}; + + +middleware.pluginHooks = function(req, res, next) { + async.each(plugins.loadedHooks['filter:router.page'] || [], function(hookObj, next) { + hookObj.method(req, res, next); + }, function() { + // If it got here, then none of the subscribed hooks did anything, or there were no hooks + next(); + }); +}; + +middleware.validateFiles = function(req, res, next) { + if (!Array.isArray(req.files.files) || !req.files.files.length) { + return next(new Error(['[[error:invalid-files]]'])); + } + + next(); +}; + +middleware.prepareAPI = function(req, res, next) { + res.locals.isAPI = true; + next(); +}; + +middleware.routeTouchIcon = function(req, res) { + if (meta.config['brand:touchIcon'] && validator.isURL(meta.config['brand:touchIcon'])) { + return res.redirect(meta.config['brand:touchIcon']); + } else { + return res.sendFile(path.join(__dirname, '../../public', meta.config['brand:touchIcon'] || '/logo.png'), { + maxAge: req.app.enabled('cache') ? 5184000000 : 0 + }); + } +}; + +middleware.privateTagListing = function(req, res, next) { + if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) { + controllers.helpers.notAllowed(req, res); + } else { + next(); + } +}; + +middleware.exposeGroupName = function(req, res, next) { + expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next); +}; + +middleware.exposeUid = function(req, res, next) { + expose('uid', user.getUidByUserslug, 'userslug', req, res, next); +}; + +function expose(exposedField, method, field, req, res, next) { + if (!req.params.hasOwnProperty(field)) { + return next(); + } + method(req.params[field], function(err, id) { + if (err) { + return next(err); + } + + res.locals[exposedField] = id; + next(); + }); } -module.exports = function(app) { - var relativePath = nconf.get('relative_path'); - - middleware = require('./middleware')(app); - - app.engine('tpl', templates.__express); - app.set('view engine', 'tpl'); - app.set('views', nconf.get('views_dir')); - app.set('json spaces', process.env.NODE_ENV === 'development' ? 4 : 0); - app.use(flash()); - - app.enable('view cache'); - - app.use(compression()); - - setupFavicon(app); - - app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon); - - app.use(bodyParser.urlencoded({extended: true})); - app.use(bodyParser.json()); - app.use(cookieParser()); - app.use(useragent.express()); - - var cookie = { - maxAge: 1000 * 60 * 60 * 24 * (parseInt(meta.config.loginDays, 10) || 14) - }; - - if (meta.config.cookieDomain) { - cookie.domain = meta.config.cookieDomain; +middleware.privateUploads = function(req, res, next) { + if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) { + return next(); } - - if (nconf.get('secure')) { - cookie.secure = true; + if (req.path.startsWith('/uploads/files')) { + return res.status(403).json('not-allowed'); } - - if (relativePath !== '') { - cookie.path = relativePath; - } - - app.use(session({ - store: db.sessionStore, - secret: nconf.get('secret'), - key: 'express.sid', - cookie: cookie, - resave: true, - saveUninitialized: true - })); - - app.use(middleware.addHeaders); - app.use(middleware.processRender); - auth.initialize(app, middleware); - - return middleware; + next(); }; + +middleware.busyCheck = function(req, res, next) { + if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) { + analytics.increment('errors:503'); + res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html')); + } else { + next(); + } +}; + +middleware.applyBlacklist = function(req, res, next) { + meta.blacklist.test(req.ip, function(err) { + next(err); + }); +}; + +middleware.processLanguages = function(req, res, next) { + var code = req.params.code; + var key = req.path.match(/[\w]+\.json/); + + if (code && key) { + languages.get(code, key[0], function(err, language) { + if (err) { + return next(err); + } + + res.status(200).json(language); + }); + } else { + res.status(404).json('{}'); + } +}; + +middleware.processTimeagoLocales = function(req, res, next) { + var fallback = req.path.indexOf('-short') === -1 ? 'jquery.timeago.en.js' : 'jquery.timeago.en-short.js', + localPath = path.join(__dirname, '../../public/vendor/jquery/timeago/locales', req.path), + exists; + + try { + exists = fs.accessSync(localPath, fs.F_OK | fs.R_OK); + } catch(e) { + exists = false; + } + + if (exists) { + res.status(200).sendFile(localPath, { + maxAge: req.app.enabled('cache') ? 5184000000 : 0 + }); + } else { + res.status(200).sendFile(path.join(__dirname, '../../public/vendor/jquery/timeago/locales', fallback), { + maxAge: req.app.enabled('cache') ? 5184000000 : 0 + }); + } +}; + + +module.exports = middleware; diff --git a/src/middleware/middleware.js b/src/middleware/middleware.js deleted file mode 100644 index e5caf9ba00..0000000000 --- a/src/middleware/middleware.js +++ /dev/null @@ -1,384 +0,0 @@ -"use strict"; - -var app; -var middleware = { - admin: {} -}; -var async = require('async'); -var fs = require('fs'); -var path = require('path'); -var csrf = require('csurf'); -var _ = require('underscore'); - -var validator = require('validator'); -var nconf = require('nconf'); -var ensureLoggedIn = require('connect-ensure-login'); -var toobusy = require('toobusy-js'); - -var plugins = require('../plugins'); -var languages = require('../languages'); -var meta = require('../meta'); -var user = require('../user'); -var groups = require('../groups'); - -var analytics = require('../analytics'); - -var controllers = { - api: require('./../controllers/api'), - helpers: require('../controllers/helpers') -}; - -toobusy.maxLag(parseInt(meta.config.eventLoopLagThreshold, 10) || 100); -toobusy.interval(parseInt(meta.config.eventLoopInterval, 10) || 500); - -middleware.authenticate = function(req, res, next) { - if (req.user) { - return next(); - } else if (plugins.hasListeners('action:middleware.authenticate')) { - return plugins.fireHook('action:middleware.authenticate', { - req: req, - res: res, - next: next - }); - } - - controllers.helpers.notAllowed(req, res); -}; - -middleware.applyCSRF = csrf(); - -middleware.ensureLoggedIn = ensureLoggedIn.ensureLoggedIn(nconf.get('relative_path') + '/login'); - -middleware.pageView = function(req, res, next) { - analytics.pageView({ - ip: req.ip, - path: req.path, - uid: req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') ? parseInt(req.user.uid, 10) : 0 - }); - - plugins.fireHook('action:middleware.pageView', {req: req}); - - if (req.user) { - user.updateLastOnlineTime(req.user.uid); - if (req.path.startsWith('/api/users') || req.path.startsWith('/users')) { - user.updateOnlineUsers(req.user.uid, next); - } else { - user.updateOnlineUsers(req.user.uid); - next(); - } - } else { - next(); - } -}; - -middleware.addHeaders = function (req, res, next) { - var defaults = { - 'X-Powered-By': 'NodeBB', - 'X-Frame-Options': 'SAMEORIGIN', - 'Access-Control-Allow-Origin': 'null' // yes, string null. - }; - var headers = { - 'X-Powered-By': meta.config['powered-by'], - 'X-Frame-Options': meta.config['allow-from-uri'] ? 'ALLOW-FROM ' + meta.config['allow-from-uri'] : undefined, - 'Access-Control-Allow-Origin': meta.config['access-control-allow-origin'], - 'Access-Control-Allow-Methods': meta.config['access-control-allow-methods'], - 'Access-Control-Allow-Headers': meta.config['access-control-allow-headers'] - }; - - _.defaults(headers, defaults); - headers = _.pick(headers, Boolean); // Remove falsy headers - - for(var key in headers) { - if (headers.hasOwnProperty(key)) { - res.setHeader(key, headers[key]); - } - } - - next(); -}; - -middleware.pluginHooks = function(req, res, next) { - async.each(plugins.loadedHooks['filter:router.page'] || [], function(hookObj, next) { - hookObj.method(req, res, next); - }, function() { - // If it got here, then none of the subscribed hooks did anything, or there were no hooks - next(); - }); -}; - -middleware.redirectToAccountIfLoggedIn = function(req, res, next) { - if (req.session.forceLogin) { - return next(); - } - - if (!req.user) { - return next(); - } - user.getUserField(req.user.uid, 'userslug', function (err, userslug) { - if (err) { - return next(err); - } - controllers.helpers.redirect(res, '/user/' + userslug); - }); -}; - -middleware.validateFiles = function(req, res, next) { - if (!Array.isArray(req.files.files) || !req.files.files.length) { - return next(new Error(['[[error:invalid-files]]'])); - } - - next(); -}; - -middleware.prepareAPI = function(req, res, next) { - res.locals.isAPI = true; - next(); -}; - -middleware.checkGlobalPrivacySettings = function(req, res, next) { - if (!req.user && !!parseInt(meta.config.privateUserInfo, 10)) { - return controllers.helpers.notAllowed(req, res); - } - - next(); -}; - -middleware.checkAccountPermissions = function(req, res, next) { - // This middleware ensures that only the requested user and admins can pass - async.waterfall([ - function (next) { - middleware.authenticate(req, res, next); - }, - function (next) { - user.getUidByUserslug(req.params.userslug, next); - }, - function (uid, next) { - if (parseInt(uid, 10) === req.uid) { - return next(null, true); - } - - user.isAdminOrGlobalMod(req.uid, next); - } - ], function (err, allowed) { - if (err || allowed) { - return next(err); - } - controllers.helpers.notAllowed(req, res); - }); -}; - -middleware.redirectUidToUserslug = function(req, res, next) { - var uid = parseInt(req.params.uid, 10); - if (!uid) { - return next(); - } - user.getUserField(uid, 'userslug', function(err, userslug) { - if (err || !userslug) { - return next(err); - } - - var path = req.path.replace(/^\/api/, '') - .replace('uid', 'user') - .replace(uid, function() { return userslug; }); - controllers.helpers.redirect(res, path); - }); -}; - -middleware.isAdmin = function(req, res, next) { - if (!req.uid) { - return controllers.helpers.notAllowed(req, res); - } - - user.isAdministrator(req.uid, function (err, isAdmin) { - if (err) { - return next(err); - } - - if (isAdmin) { - user.hasPassword(req.uid, function(err, hasPassword) { - if (err) { - return next(err); - } - - if (!hasPassword) { - return next(); - } - - var loginTime = req.session.meta ? req.session.meta.datetime : 0; - if (loginTime && parseInt(loginTime, 10) > Date.now() - 3600000) { - var timeLeft = parseInt(loginTime, 10) - (Date.now() - 3600000); - if (timeLeft < 300000) { - req.session.meta.datetime += 300000; - } - - return next(); - } - - req.session.returnTo = req.path.replace(/^\/api/, ''); - req.session.forceLogin = 1; - if (res.locals.isAPI) { - res.status(401).json({}); - } else { - res.redirect(nconf.get('relative_path') + '/login'); - } - }); - return; - } - - if (res.locals.isAPI) { - return controllers.helpers.notAllowed(req, res); - } - - middleware.buildHeader(req, res, function() { - controllers.helpers.notAllowed(req, res); - }); - }); -}; - -middleware.routeTouchIcon = function(req, res) { - if (meta.config['brand:touchIcon'] && validator.isURL(meta.config['brand:touchIcon'])) { - return res.redirect(meta.config['brand:touchIcon']); - } else { - return res.sendFile(path.join(__dirname, '../../public', meta.config['brand:touchIcon'] || '/logo.png'), { - maxAge: app.enabled('cache') ? 5184000000 : 0 - }); - } -}; - -middleware.addExpiresHeaders = function(req, res, next) { - if (app.enabled('cache')) { - res.setHeader("Cache-Control", "public, max-age=5184000"); - res.setHeader("Expires", new Date(Date.now() + 5184000000).toUTCString()); - } else { - res.setHeader("Cache-Control", "public, max-age=0"); - res.setHeader("Expires", new Date().toUTCString()); - } - - next(); -}; - -middleware.privateTagListing = function(req, res, next) { - if (!req.user && parseInt(meta.config.privateTagListing, 10) === 1) { - controllers.helpers.notAllowed(req, res); - } else { - next(); - } -}; - -middleware.exposeGroupName = function(req, res, next) { - expose('groupName', groups.getGroupNameByGroupSlug, 'slug', req, res, next); -}; - -middleware.exposeUid = function(req, res, next) { - expose('uid', user.getUidByUserslug, 'userslug', req, res, next); -}; - -function expose(exposedField, method, field, req, res, next) { - if (!req.params.hasOwnProperty(field)) { - return next(); - } - method(req.params[field], function(err, id) { - if (err) { - return next(err); - } - - res.locals[exposedField] = id; - next(); - }); -} - -middleware.requireUser = function(req, res, next) { - if (req.user) { - return next(); - } - - res.status(403).render('403', {title: '[[global:403.title]]'}); -}; - -middleware.privateUploads = function(req, res, next) { - if (req.user || parseInt(meta.config.privateUploads, 10) !== 1) { - return next(); - } - if (req.path.startsWith('/uploads/files')) { - return res.status(403).json('not-allowed'); - } - next(); -}; - -middleware.busyCheck = function(req, res, next) { - if (global.env === 'production' && (!meta.config.hasOwnProperty('eventLoopCheckEnabled') || parseInt(meta.config.eventLoopCheckEnabled, 10) === 1) && toobusy()) { - analytics.increment('errors:503'); - res.status(503).type('text/html').sendFile(path.join(__dirname, '../../public/503.html')); - } else { - next(); - } -}; - -middleware.applyBlacklist = function(req, res, next) { - meta.blacklist.test(req.ip, function(err) { - next(err); - }); -}; - -middleware.processLanguages = function(req, res, next) { - var code = req.params.code; - var key = req.path.match(/[\w]+\.json/); - - if (code && key) { - languages.get(code, key[0], function(err, language) { - if (err) { - return next(err); - } - - res.status(200).json(language); - }); - } else { - res.status(404).json('{}'); - } -}; - -middleware.processTimeagoLocales = function(req, res, next) { - var fallback = req.path.indexOf('-short') === -1 ? 'jquery.timeago.en.js' : 'jquery.timeago.en-short.js', - localPath = path.join(__dirname, '../../public/vendor/jquery/timeago/locales', req.path), - exists; - - try { - exists = fs.accessSync(localPath, fs.F_OK | fs.R_OK); - } catch(e) { - exists = false; - } - - if (exists) { - res.status(200).sendFile(localPath, { - maxAge: app.enabled('cache') ? 5184000000 : 0 - }); - } else { - res.status(200).sendFile(path.join(__dirname, '../../public/vendor/jquery/timeago/locales', fallback), { - maxAge: app.enabled('cache') ? 5184000000 : 0 - }); - } -}; - -middleware.registrationComplete = function(req, res, next) { - // If the user's session contains registration data, redirect the user to complete registration - if (!req.session.hasOwnProperty('registration')) { - return next(); - } else { - if (!req.path.endsWith('/register/complete')) { - controllers.helpers.redirect(res, '/register/complete'); - } else { - return next(); - } - } -}; - -module.exports = function(webserver) { - app = webserver; - middleware.admin = require('./admin')(webserver); - - require('./header')(app, middleware); - require('./render')(middleware); - require('./maintenance')(middleware); - - return middleware; -}; diff --git a/src/middleware/user.js b/src/middleware/user.js new file mode 100644 index 0000000000..b70f7639d7 --- /dev/null +++ b/src/middleware/user.js @@ -0,0 +1,154 @@ +'use strict'; + +var async = require('async'); +var nconf = require('nconf'); +var meta = require('../meta'); +var user = require('../user'); + +var controllers = { + helpers: require('../controllers/helpers') +}; + +module.exports = function(middleware) { + + middleware.checkGlobalPrivacySettings = function(req, res, next) { + if (!req.user && !!parseInt(meta.config.privateUserInfo, 10)) { + return controllers.helpers.notAllowed(req, res); + } + + next(); + }; + + middleware.checkAccountPermissions = function(req, res, next) { + // This middleware ensures that only the requested user and admins can pass + async.waterfall([ + function (next) { + middleware.authenticate(req, res, next); + }, + function (next) { + user.getUidByUserslug(req.params.userslug, next); + }, + function (uid, next) { + if (parseInt(uid, 10) === req.uid) { + return next(null, true); + } + + user.isAdminOrGlobalMod(req.uid, next); + } + ], function (err, allowed) { + if (err || allowed) { + return next(err); + } + controllers.helpers.notAllowed(req, res); + }); + }; + + middleware.redirectToAccountIfLoggedIn = function(req, res, next) { + if (req.session.forceLogin) { + return next(); + } + + if (!req.user) { + return next(); + } + user.getUserField(req.user.uid, 'userslug', function (err, userslug) { + if (err) { + return next(err); + } + controllers.helpers.redirect(res, '/user/' + userslug); + }); + }; + + middleware.redirectUidToUserslug = function(req, res, next) { + var uid = parseInt(req.params.uid, 10); + if (!uid) { + return next(); + } + user.getUserField(uid, 'userslug', function(err, userslug) { + if (err || !userslug) { + return next(err); + } + + var path = req.path.replace(/^\/api/, '') + .replace('uid', 'user') + .replace(uid, function() { return userslug; }); + controllers.helpers.redirect(res, path); + }); + }; + + middleware.isAdmin = function(req, res, next) { + if (!req.uid) { + return controllers.helpers.notAllowed(req, res); + } + + user.isAdministrator(req.uid, function (err, isAdmin) { + if (err) { + return next(err); + } + + if (isAdmin) { + user.hasPassword(req.uid, function(err, hasPassword) { + if (err) { + return next(err); + } + + if (!hasPassword) { + return next(); + } + + var loginTime = req.session.meta ? req.session.meta.datetime : 0; + if (loginTime && parseInt(loginTime, 10) > Date.now() - 3600000) { + var timeLeft = parseInt(loginTime, 10) - (Date.now() - 3600000); + if (timeLeft < 300000) { + req.session.meta.datetime += 300000; + } + + return next(); + } + + req.session.returnTo = req.path.replace(/^\/api/, ''); + req.session.forceLogin = 1; + if (res.locals.isAPI) { + res.status(401).json({}); + } else { + res.redirect(nconf.get('relative_path') + '/login'); + } + }); + return; + } + + if (res.locals.isAPI) { + return controllers.helpers.notAllowed(req, res); + } + + middleware.buildHeader(req, res, function() { + controllers.helpers.notAllowed(req, res); + }); + }); + }; + + middleware.requireUser = function(req, res, next) { + if (req.user) { + return next(); + } + + res.status(403).render('403', {title: '[[global:403.title]]'}); + }; + + middleware.registrationComplete = function(req, res, next) { + // If the user's session contains registration data, redirect the user to complete registration + if (!req.session.hasOwnProperty('registration')) { + return next(); + } else { + if (!req.path.endsWith('/register/complete')) { + controllers.helpers.redirect(res, '/register/complete'); + } else { + return next(); + } + } + }; + +}; + + + diff --git a/src/plugins.js b/src/plugins.js index c531dfa27d..fc56d799c6 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -10,12 +10,10 @@ var nconf = require('nconf'); var db = require('./database'); var emitter = require('./emitter'); -var translator = require('../public/src/modules/translator'); var utils = require('../public/src/utils'); var hotswap = require('./hotswap'); var file = require('./file'); -var controllers = require('./controllers'); var app; var middleware; @@ -149,11 +147,13 @@ var middleware; Plugins.reloadRoutes = function(callback) { callback = callback || function() {}; var router = express.Router(); + router.hotswapId = 'plugins'; router.render = function() { app.render.apply(app, arguments); }; + var controllers = require('./controllers'); Plugins.fireHook('static:app.load', {app: app, router: router, middleware: middleware, controllers: controllers}, function(err) { if (err) { return winston.error('[plugins] Encountered error while executing post-router plugins hooks: ' + err.message); diff --git a/src/webserver.js b/src/webserver.js index 5ed7f44cd6..65b1eae0ff 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -1,25 +1,35 @@ 'use strict'; -var path = require('path'), - fs = require('fs'), - nconf = require('nconf'), - express = require('express'), - app = express(), - server, - winston = require('winston'), - async = require('async'), +var fs = require('fs'); +var path = require('path'); +var nconf = require('nconf'); +var express = require('express'); +var app = express(); +var server; +var winston = require('winston'); +var async = require('async'); +var flash = require('connect-flash'); +var compression = require('compression'); +var bodyParser = require('body-parser'); +var cookieParser = require('cookie-parser'); +var session = require('express-session'); +var useragent = require('express-useragent'); - emailer = require('./emailer'), - meta = require('./meta'), - languages = require('./languages'), - logger = require('./logger'), - plugins = require('./plugins'), - middleware = require('./middleware'), - routes = require('./routes'), - emitter = require('./emitter'), +var db = require('./database'); +var file = require('./file'); +var emailer = require('./emailer'); +var meta = require('./meta'); +var languages = require('./languages'); +var logger = require('./logger'); +var plugins = require('./plugins'); +var middleware = require('./middleware'); +var routes = require('./routes'); +var auth = require('./routes/authentication'); +var emitter = require('./emitter'); +var templates = require('templates.js'); - helpers = require('../public/src/modules/helpers'); +var helpers = require('../public/src/modules/helpers'); if (nconf.get('ssl')) { server = require('https').createServer({ @@ -46,7 +56,7 @@ server.on('error', function(err) { module.exports.listen = function() { emailer.registerApp(app); - app.locals.middleware = middleware = middleware(app); + setupExpressApp(app); helpers.register(); @@ -71,6 +81,69 @@ module.exports.listen = function() { }); }; +function setupExpressApp(app) { + var relativePath = nconf.get('relative_path'); + + app.engine('tpl', templates.__express); + app.set('view engine', 'tpl'); + app.set('views', nconf.get('views_dir')); + app.set('json spaces', process.env.NODE_ENV === 'development' ? 4 : 0); + app.use(flash()); + + app.enable('view cache'); + + app.use(compression()); + + setupFavicon(app); + + app.use(relativePath + '/apple-touch-icon', middleware.routeTouchIcon); + + app.use(bodyParser.urlencoded({extended: true})); + app.use(bodyParser.json()); + app.use(cookieParser()); + app.use(useragent.express()); + + var cookie = { + maxAge: 1000 * 60 * 60 * 24 * (parseInt(meta.config.loginDays, 10) || 14) + }; + + if (meta.config.cookieDomain) { + cookie.domain = meta.config.cookieDomain; + } + + if (nconf.get('secure')) { + cookie.secure = true; + } + + if (relativePath !== '') { + cookie.path = relativePath; + } + + app.use(session({ + store: db.sessionStore, + secret: nconf.get('secret'), + key: 'express.sid', + cookie: cookie, + resave: true, + saveUninitialized: true + })); + + app.use(middleware.addHeaders); + app.use(middleware.processRender); + auth.initialize(app, middleware); + + var toobusy = require('toobusy-js'); + toobusy.maxLag(parseInt(meta.config.eventLoopLagThreshold, 10) || 100); + toobusy.interval(parseInt(meta.config.eventLoopInterval, 10) || 500); +} + +function setupFavicon(app) { + var faviconPath = path.join(__dirname, '../../', 'public', meta.config['brand:favicon'] ? meta.config['brand:favicon'] : 'favicon.ico'); + if (file.existsSync(faviconPath)) { + app.use(nconf.get('relative_path'), favicon(faviconPath)); + } +} + function initializeNodeBB(callback) { var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; From a0f2e8a8ec82ebfbe2ad54c911b55ece7ff2e714 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 18:55:44 +0300 Subject: [PATCH 49/86] use req.uid :+1: --- src/middleware/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/index.js b/src/middleware/index.js index 266f2b1488..7d5b5a4e80 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -53,7 +53,7 @@ middleware.pageView = function(req, res, next) { analytics.pageView({ ip: req.ip, path: req.path, - uid: req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') ? parseInt(req.user.uid, 10) : 0 + uid: req.uid }); plugins.fireHook('action:middleware.pageView', {req: req}); From df7cfe21469b0e1d6327d788103b34bd526e26e4 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 19:12:55 +0300 Subject: [PATCH 50/86] missing favicon dep --- src/webserver.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/webserver.js b/src/webserver.js index 65b1eae0ff..ec80bf5f17 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -15,6 +15,7 @@ var bodyParser = require('body-parser'); var cookieParser = require('cookie-parser'); var session = require('express-session'); var useragent = require('express-useragent'); +var favicon = require('serve-favicon'); var db = require('./database'); var file = require('./file'); @@ -145,7 +146,7 @@ function setupFavicon(app) { } function initializeNodeBB(callback) { - var skipJS, skipLess, fromFile = nconf.get('from-file') || ''; + var skipJS, fromFile = nconf.get('from-file') || ''; if (fromFile.match('js')) { winston.info('[minifier] Minifying client-side JS skipped'); @@ -195,7 +196,7 @@ function cacheStaticFiles(callback) { callback(); } -function listen(callback) { +function listen() { var port = parseInt(nconf.get('port'), 10); if (Array.isArray(port)) { From 6d946425fa7649eb1726167d39ef848359492688 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 19:13:05 +0300 Subject: [PATCH 51/86] fix tests, circular deps --- src/categories.js | 4 +++- src/categories/create.js | 5 +++-- src/privileges/categories.js | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/categories.js b/src/categories.js index 539a643f80..46af3f1c5a 100644 --- a/src/categories.js +++ b/src/categories.js @@ -7,7 +7,7 @@ var db = require('./database'); var user = require('./user'); var Groups = require('./groups'); var plugins = require('./plugins'); -var privileges = require('./privileges'); + (function(Categories) { @@ -102,6 +102,7 @@ var privileges = require('./privileges'); }; Categories.getCategoriesByPrivilege = function(set, uid, privilege, callback) { + var privileges = require('./privileges'); async.waterfall([ function(next) { db.getSortedSetRange(set, 0, -1, next); @@ -238,6 +239,7 @@ var privileges = require('./privileges'); }; function getChildrenRecursive(category, uid, callback) { + var privileges = require('./privileges'); async.waterfall([ function (next) { db.getSortedSetRange('cid:' + category.cid + ':children', 0, -1, next); diff --git a/src/categories/create.js b/src/categories/create.js index e2d6960e45..09df61cb07 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -3,7 +3,7 @@ var async = require('async'); var db = require('../database'); -var privileges = require('../privileges'); + var groups = require('../groups'); var plugins = require('../plugins'); var utils = require('../../public/src/utils'); @@ -50,7 +50,7 @@ module.exports = function(Categories) { category = data.category; var defaultPrivileges = ['find', 'read', 'topics:read', 'topics:create', 'topics:reply', 'posts:edit', 'posts:delete', 'topics:delete', 'upload:post:image']; - + var privileges = require('../privileges'); async.series([ async.apply(db.setObject, 'category:' + category.cid, category), function (next) { @@ -139,6 +139,7 @@ module.exports = function(Categories) { }; Categories.copyPrivilegesFrom = function(fromCid, toCid, callback) { + var privileges = require('../privileges'); async.each(privileges.privilegeList, function(privilege, next) { copyPrivilege(privilege, fromCid, toCid, next); }, callback); diff --git a/src/privileges/categories.js b/src/privileges/categories.js index 0baf08af91..bebb50f4a9 100644 --- a/src/privileges/categories.js +++ b/src/privileges/categories.js @@ -5,7 +5,6 @@ var async = require('async'); var _ = require('underscore'); var user = require('../user'); -var categories = require('../categories'); var groups = require('../groups'); var helpers = require('./helpers'); var plugins = require('../plugins'); @@ -220,6 +219,7 @@ module.exports = function(privileges) { if (!cid) { return callback(null, false); } + var categories = require('../categories'); categories.getCategoryField(cid, 'disabled', function(err, disabled) { if (err) { return callback(err); @@ -269,6 +269,7 @@ module.exports = function(privileges) { }; privileges.categories.getBase = function(privilege, cids, uid, callback) { + var categories = require('../categories'); async.parallel({ categories: function(next) { categories.getCategoriesFields(cids, ['disabled'], next); From 56c4e13316cd8df80f314470a3018b176700b695 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 19:33:16 +0300 Subject: [PATCH 52/86] another dependency fix and test --- src/categories/delete.js | 2 +- src/categories/recentreplies.js | 6 +++--- src/categories/unread.js | 4 ++-- src/categories/update.js | 10 +++++----- tests/categories.js | 19 +++++++++++++++++++ 5 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/categories/delete.js b/src/categories/delete.js index 63f9fe324b..e7713bb426 100644 --- a/src/categories/delete.js +++ b/src/categories/delete.js @@ -5,7 +5,6 @@ var db = require('../database'); var batch = require('../batch'); var plugins = require('../plugins'); var topics = require('../topics'); -var privileges = require('../privileges'); var groups = require('../groups'); module.exports = function(Categories) { @@ -46,6 +45,7 @@ module.exports = function(Categories) { ], next); }, function(next) { + var privileges = require('../privileges'); async.each(privileges.privilegeList, function(privilege, next) { groups.destroy('cid:' + cid + ':privileges:' + privilege, next); }, next); diff --git a/src/categories/recentreplies.js b/src/categories/recentreplies.js index 7a5e1cc1e8..bd89939078 100644 --- a/src/categories/recentreplies.js +++ b/src/categories/recentreplies.js @@ -10,7 +10,7 @@ var db = require('../database'); var posts = require('../posts'); var topics = require('../topics'); var categories = require('../categories'); -var privileges = require('../privileges'); + module.exports = function(Categories) { @@ -18,7 +18,7 @@ module.exports = function(Categories) { if (!parseInt(count, 10)) { return callback(null, []); } - + var privileges = require('../privileges'); async.waterfall([ function(next) { db.getSortedSetRevRange('cid:' + cid + ':pids', 0, count - 1, next); @@ -36,7 +36,7 @@ module.exports = function(Categories) { if (!Array.isArray(categoryData) || !categoryData.length) { return callback(); } - + var privileges = require('../privileges'); async.waterfall([ function(next) { async.map(categoryData, getRecentTopicTids, next); diff --git a/src/categories/unread.js b/src/categories/unread.js index d62aaa3c6e..37496e09b7 100644 --- a/src/categories/unread.js +++ b/src/categories/unread.js @@ -1,8 +1,8 @@ "use strict"; -var async = require('async'), - db = require('../database'); +var async = require('async'); +var db = require('../database'); module.exports = function(Categories) { diff --git a/src/categories/update.js b/src/categories/update.js index 78e97e4076..7c45663073 100644 --- a/src/categories/update.js +++ b/src/categories/update.js @@ -1,11 +1,11 @@ 'use strict'; -var async = require('async'), - db = require('../database'), - utils = require('../../public/src/utils'), - translator = require('../../public/src/modules/translator'), - plugins = require('../plugins'); +var async = require('async'); +var db = require('../database'); +var utils = require('../../public/src/utils'); +var translator = require('../../public/src/modules/translator'); +var plugins = require('../plugins'); module.exports = function(Categories) { diff --git a/tests/categories.js b/tests/categories.js index b607e6fc0a..9e762f5a90 100644 --- a/tests/categories.js +++ b/tests/categories.js @@ -54,6 +54,25 @@ describe('Categories', function() { }); }); + describe('Categories.getRecentTopicReplies', function() { + it('should not throw', function(done) { + Categories.getCategoryById({ + cid: categoryObj.cid, + set: 'cid:' + categoryObj.cid + ':tids', + reverse: true, + start: 0, + stop: -1, + uid: 0 + }, function(err, categoryData) { + assert.ifError(err); + Categories.getRecentTopicReplies(categoryData, 0, function(err) { + assert.ifError(err); + done(); + }); + }); + }); + }); + describe('.getCategoryTopics', function() { it('should return a list of topics', function(done) { Categories.getCategoryTopics({ From 89f550ce7cb4cbfecdf40d73f38c773310784ef7 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Fri, 26 Aug 2016 19:40:14 +0300 Subject: [PATCH 53/86] moved middleware requires --- src/categories/create.js | 1 - src/webserver.js | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/categories/create.js b/src/categories/create.js index 09df61cb07..fc841679d2 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -3,7 +3,6 @@ var async = require('async'); var db = require('../database'); - var groups = require('../groups'); var plugins = require('../plugins'); var utils = require('../../public/src/utils'); diff --git a/src/webserver.js b/src/webserver.js index ec80bf5f17..68ba499858 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -24,7 +24,6 @@ var meta = require('./meta'); var languages = require('./languages'); var logger = require('./logger'); var plugins = require('./plugins'); -var middleware = require('./middleware'); var routes = require('./routes'); var auth = require('./routes/authentication'); var emitter = require('./emitter'); @@ -83,6 +82,8 @@ module.exports.listen = function() { }; function setupExpressApp(app) { + var middleware = require('./middleware'); + var relativePath = nconf.get('relative_path'); app.engine('tpl', templates.__express); @@ -146,7 +147,9 @@ function setupFavicon(app) { } function initializeNodeBB(callback) { - var skipJS, fromFile = nconf.get('from-file') || ''; + var skipJS; + var fromFile = nconf.get('from-file') || ''; + var middleware = require('./middleware'); if (fromFile.match('js')) { winston.info('[minifier] Minifying client-side JS skipped'); From 785f4fc5a098ae01de48089bff56a6030b2df480 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Fri, 26 Aug 2016 15:36:42 -0400 Subject: [PATCH 54/86] updating to jQuery 3.x on frontend and ACP --- package.json | 1 + public/less/admin/admin.less | 6 + public/less/admin/manage/tags.less | 11 +- public/less/admin/manage/users.less | 9 + public/src/admin/manage/flags.js | 3 +- public/src/admin/manage/tags.js | 4 +- public/src/admin/manage/users.js | 14 +- public/src/admin/modules/selectable.js | 141 +- public/src/modules/translator.js | 4 +- public/vendor/bootstrap/js/bootstrap.js | 2377 ++++ public/vendor/bootstrap/js/bootstrap.min.js | 7 - .../smoothness/images/animated-overlay.gif | Bin 1738 -> 0 bytes .../images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 212 -> 0 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 208 -> 0 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 335 -> 335 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 207 -> 207 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 262 -> 262 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 262 -> 262 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 332 -> 332 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 280 -> 280 bytes .../images/ui-icons_222222_256x240.png | Bin 6922 -> 6922 bytes .../images/ui-icons_2e83ff_256x240.png | Bin 4549 -> 4549 bytes .../images/ui-icons_454545_256x240.png | Bin 6992 -> 6992 bytes .../images/ui-icons_888888_256x240.png | Bin 6999 -> 6999 bytes .../images/ui-icons_cd0a0a_256x240.png | Bin 4549 -> 4549 bytes .../jquery-ui-1.10.4.custom.min.css | 7 - .../jquery/css/smoothness/jquery-ui.css | 845 ++ .../jquery/js/jquery-ui-1.10.4.custom.js | 7 - public/vendor/jquery/js/jquery-ui.js | 9857 +++++++++++++++++ src/meta/css.js | 7 +- src/meta/js.js | 5 +- src/views/admin/header.tpl | 4 - src/views/admin/manage/users.tpl | 4 +- 33 files changed, 13199 insertions(+), 114 deletions(-) create mode 100644 public/vendor/bootstrap/js/bootstrap.js delete mode 100644 public/vendor/bootstrap/js/bootstrap.min.js delete mode 100644 public/vendor/jquery/css/smoothness/images/animated-overlay.gif delete mode 100644 public/vendor/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png delete mode 100644 public/vendor/jquery/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png delete mode 100644 public/vendor/jquery/css/smoothness/jquery-ui-1.10.4.custom.min.css create mode 100644 public/vendor/jquery/css/smoothness/jquery-ui.css delete mode 100644 public/vendor/jquery/js/jquery-ui-1.10.4.custom.js create mode 100644 public/vendor/jquery/js/jquery-ui.js diff --git a/package.json b/package.json index 97c7ca45fa..cf80e99e15 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "html-to-text": "2.0.0", "ip": "1.1.2", "jimp": "0.2.21", + "jquery": "^3.1.0", "json-2-csv": "^2.0.22", "less": "^2.0.0", "logrotate-stream": "^0.2.3", diff --git a/public/less/admin/admin.less b/public/less/admin/admin.less index ed15e562e4..0ceeb050e5 100644 --- a/public/less/admin/admin.less +++ b/public/less/admin/admin.less @@ -265,4 +265,10 @@ body { [class^="col-"] .mdl-switch__label { padding-right: 15px; +} + +.ui-selectable-helper { + border: 1px dashed @brand-success; + background: lighten(@brand-success, 10%); + opacity: 0.5; } \ No newline at end of file diff --git a/public/less/admin/manage/tags.less b/public/less/admin/manage/tags.less index 6800778237..34075816b1 100644 --- a/public/less/admin/manage/tags.less +++ b/public/less/admin/manage/tags.less @@ -5,14 +5,23 @@ } .tag-row { - padding: 5px; + padding: 0.5rem; float: left; + margin-left: 0.5rem; .tag-item { cursor: pointer; display: inline-block; font-size: 11px; } + + &.ui-selected { + background: lighten(@brand-success, 25%); + } + + &.ui-selecting { + background: lighten(@brand-success, 40%); + } } } diff --git a/public/less/admin/manage/users.less b/public/less/admin/manage/users.less index bed33b77a1..0ab74c7544 100644 --- a/public/less/admin/manage/users.less +++ b/public/less/admin/manage/users.less @@ -16,6 +16,7 @@ height: auto; max-width: 145px; min-width: 145px; + padding: 1rem; img, .user-icon { .user-icon-style(80px, 4rem); @@ -44,5 +45,13 @@ } } } + + .ui-selected { + background: lighten(@brand-success, 25%); + } + + .ui-selecting { + background: lighten(@brand-success, 40%); + } } } \ No newline at end of file diff --git a/public/src/admin/manage/flags.js b/public/src/admin/manage/flags.js index 7a89c24845..a686c4a07e 100644 --- a/public/src/admin/manage/flags.js +++ b/public/src/admin/manage/flags.js @@ -3,10 +3,9 @@ define('admin/manage/flags', [ 'forum/infinitescroll', - 'admin/modules/selectable', 'autocomplete', 'Chart' -], function(infinitescroll, selectable, autocomplete, Chart) { +], function(infinitescroll, autocomplete, Chart) { var Flags = {}; diff --git a/public/src/admin/manage/tags.js b/public/src/admin/manage/tags.js index 9fda81eccb..0c0bc368c3 100644 --- a/public/src/admin/manage/tags.js +++ b/public/src/admin/manage/tags.js @@ -80,7 +80,7 @@ define('admin/manage/tags', [ function handleModify() { $('#modify').on('click', function() { - var tagsToModify = $('.tag-row.selected'); + var tagsToModify = $('.tag-row.ui-selected'); if (!tagsToModify.length) { return; } @@ -120,7 +120,7 @@ define('admin/manage/tags', [ function handleDeleteSelected() { $('#deleteSelected').on('click', function() { - var tagsToDelete = $('.tag-row.selected'); + var tagsToDelete = $('.tag-row.ui-selected'); if (!tagsToDelete.length) { return; } diff --git a/public/src/admin/manage/users.js b/public/src/admin/manage/users.js index 1008928bc5..dfda934106 100644 --- a/public/src/admin/manage/users.js +++ b/public/src/admin/manage/users.js @@ -6,29 +6,29 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) var Users = {}; Users.init = function() { - selectable.enable('#users-container', '.user-selectable'); + selectable.enable('#users-container', '.users-box'); function getSelectedUids() { var uids = []; - $('#users-container .users-box .selected').each(function() { - uids.push($(this).parents('[data-uid]').attr('data-uid')); + $('#users-container .users-box.ui-selected').each(function() { + uids.push(this.getAttribute('data-uid')); }); return uids; } function update(className, state) { - $('#users-container .users-box .selected').siblings('.labels').find(className).each(function() { + $('#users-container .users-box.ui-selected .labels').find(className).each(function() { $(this).toggleClass('hide', !state); }); } function unselectAll() { - $('#users-container .users-box .selected').removeClass('selected'); + $('#users-container .users-box.ui-selected').removeClass('ui-selected'); } function removeSelected() { - $('#users-container .users-box .selected').parents('.users-box').remove(); + $('#users-container .users-box.ui-selected').remove(); } function done(successMessage, className, flag) { @@ -328,7 +328,7 @@ define('admin/manage/users', ['admin/modules/selectable'], function(selectable) .removeClass('label-danger'); } - selectable.enable('#users-container', '.user-selectable'); + selectable.enable('#users-container', '.users-box'); }); }); }, 250); diff --git a/public/src/admin/modules/selectable.js b/public/src/admin/modules/selectable.js index 81b4fcf06f..cce19bcdda 100644 --- a/public/src/admin/modules/selectable.js +++ b/public/src/admin/modules/selectable.js @@ -5,76 +5,81 @@ define('admin/modules/selectable', function() { var selectable = {}; - // modified from http://threedubmedia.com/code/event/drop/demo/selection - selectable.enable = function(parentElement, elementsToSelect, events) { - function selected(element) { - var $element = $(element).toggleClass('selected'); - - if (events && typeof events.onSelected === 'function') { - events.onSelected($element); - } - } - - function unselected(element) { - var $element = $(element).removeClass('selected'); - - if (events && typeof events.onUnselected === 'function') { - events.onUnselected($element); - } - } - - parentElement = $(parentElement); - elementsToSelect = $(elementsToSelect).not('.selection'); - - var offset = parentElement.offset(); - - parentElement - .addClass('selectable') - .on('mousedown', function(ev) { - if (!ev.shiftKey) { - unselected(elementsToSelect); - } - }) - .drag('start',function(ev, dd) { - if (!ev.shiftKey) { - unselected(elementsToSelect); - } - - return $('
') - .css('opacity', 0.65 ) - .appendTo(parentElement); - }) - .drag(function(ev, dd){ - $(dd.proxy).css({ - top: Math.min(ev.pageY - offset.top, dd.startY - offset.top), - left: Math.min(ev.pageX - offset.left, dd.startX - offset.left), - height: Math.abs(ev.pageY - dd.startY), - width: Math.abs(ev.pageX - dd.startX) - }); - }) - .drag('end',function(ev, dd){ - $(dd.proxy).remove(); - }); - - elementsToSelect - .addClass('selection') - .on('mouseup', function(ev) { - selected(this); - }) - .drop('start',function(){ - $(this).addClass('active'); - }) - .drop(function( ev, dd ){ - selected(this); - }) - .drop('end',function(){ - $(this).removeClass('active'); - }); - - $.drop({ - multi: true + selectable.enable = function(containerEl, targets) { + $(containerEl).selectable({ + filter: targets }); }; + // // modified from http://threedubmedia.com/code/event/drop/demo/selection + // selectable.enable = function(parentElement, elementsToSelect, events) { + // function selected(element) { + // var $element = $(element).toggleClass('selected'); + + // if (events && typeof events.onSelected === 'function') { + // events.onSelected($element); + // } + // } + + // function unselected(element) { + // var $element = $(element).removeClass('selected'); + + // if (events && typeof events.onUnselected === 'function') { + // events.onUnselected($element); + // } + // } + + // parentElement = $(parentElement); + // elementsToSelect = $(elementsToSelect).not('.selection'); + + // var offset = parentElement.offset(); + + // parentElement + // .addClass('selectable') + // .on('mousedown', function(ev) { + // if (!ev.shiftKey) { + // unselected(elementsToSelect); + // } + // }) + // .drag('start',function(ev, dd) { + // if (!ev.shiftKey) { + // unselected(elementsToSelect); + // } + + // return $('
') + // .css('opacity', 0.65 ) + // .appendTo(parentElement); + // }) + // .drag(function(ev, dd){ + // $(dd.proxy).css({ + // top: Math.min(ev.pageY - offset.top, dd.startY - offset.top), + // left: Math.min(ev.pageX - offset.left, dd.startX - offset.left), + // height: Math.abs(ev.pageY - dd.startY), + // width: Math.abs(ev.pageX - dd.startX) + // }); + // }) + // .drag('end',function(ev, dd){ + // $(dd.proxy).remove(); + // }); + + // elementsToSelect + // .addClass('selection') + // .on('mouseup', function(ev) { + // selected(this); + // }) + // .drop('start',function(){ + // $(this).addClass('active'); + // }) + // .drop(function( ev, dd ){ + // selected(this); + // }) + // .drop('end',function(){ + // $(this).removeClass('active'); + // }); + + // $.drop({ + // multi: true + // }); + // }; return selectable; }); diff --git a/public/src/modules/translator.js b/public/src/modules/translator.js index ad18721605..00a9126448 100644 --- a/public/src/modules/translator.js +++ b/public/src/modules/translator.js @@ -96,12 +96,12 @@ break; } - $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').success(function() { + $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').done(function() { $('.timeago').timeago(); translator.timeagoShort = $.extend({}, jQuery.timeago.settings.strings); // Retrieve the shorthand timeago values as well - $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').success(function() { + $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '-short.js').done(function() { // Switch back to long-form translator.toggleTimeagoShorthand(); }); diff --git a/public/vendor/bootstrap/js/bootstrap.js b/public/vendor/bootstrap/js/bootstrap.js new file mode 100644 index 0000000000..8a2e99a535 --- /dev/null +++ b/public/vendor/bootstrap/js/bootstrap.js @@ -0,0 +1,2377 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the MIT license + */ + +if (typeof jQuery === 'undefined') { + throw new Error('Bootstrap\'s JavaScript requires jQuery') +} + ++function ($) { + 'use strict'; + var version = $.fn.jquery.split(' ')[0].split('.') + if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1) || (version[0] > 3)) { + throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher, but lower than version 4') + } +}(jQuery); + +/* ======================================================================== + * Bootstrap: transition.js v3.3.7 + * http://getbootstrap.com/javascript/#transitions + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) + // ============================================================ + + function transitionEnd() { + var el = document.createElement('bootstrap') + + var transEndEventNames = { + WebkitTransition : 'webkitTransitionEnd', + MozTransition : 'transitionend', + OTransition : 'oTransitionEnd otransitionend', + transition : 'transitionend' + } + + for (var name in transEndEventNames) { + if (el.style[name] !== undefined) { + return { end: transEndEventNames[name] } + } + } + + return false // explicit for ie8 ( ._.) + } + + // http://blog.alexmaccaw.com/css-transitions + $.fn.emulateTransitionEnd = function (duration) { + var called = false + var $el = this + $(this).one('bsTransitionEnd', function () { called = true }) + var callback = function () { if (!called) $($el).trigger($.support.transition.end) } + setTimeout(callback, duration) + return this + } + + $(function () { + $.support.transition = transitionEnd() + + if (!$.support.transition) return + + $.event.special.bsTransitionEnd = { + bindType: $.support.transition.end, + delegateType: $.support.transition.end, + handle: function (e) { + if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) + } + } + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: alert.js v3.3.7 + * http://getbootstrap.com/javascript/#alerts + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // ALERT CLASS DEFINITION + // ====================== + + var dismiss = '[data-dismiss="alert"]' + var Alert = function (el) { + $(el).on('click', dismiss, this.close) + } + + Alert.VERSION = '3.3.7' + + Alert.TRANSITION_DURATION = 150 + + Alert.prototype.close = function (e) { + var $this = $(this) + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = $(selector === '#' ? [] : selector) + + if (e) e.preventDefault() + + if (!$parent.length) { + $parent = $this.closest('.alert') + } + + $parent.trigger(e = $.Event('close.bs.alert')) + + if (e.isDefaultPrevented()) return + + $parent.removeClass('in') + + function removeElement() { + // detach from parent, fire event then clean up data + $parent.detach().trigger('closed.bs.alert').remove() + } + + $.support.transition && $parent.hasClass('fade') ? + $parent + .one('bsTransitionEnd', removeElement) + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : + removeElement() + } + + + // ALERT PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.alert') + + if (!data) $this.data('bs.alert', (data = new Alert(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.alert + + $.fn.alert = Plugin + $.fn.alert.Constructor = Alert + + + // ALERT NO CONFLICT + // ================= + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + + // ALERT DATA-API + // ============== + + $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: button.js v3.3.7 + * http://getbootstrap.com/javascript/#buttons + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // BUTTON PUBLIC CLASS DEFINITION + // ============================== + + var Button = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Button.DEFAULTS, options) + this.isLoading = false + } + + Button.VERSION = '3.3.7' + + Button.DEFAULTS = { + loadingText: 'loading...' + } + + Button.prototype.setState = function (state) { + var d = 'disabled' + var $el = this.$element + var val = $el.is('input') ? 'val' : 'html' + var data = $el.data() + + state += 'Text' + + if (data.resetText == null) $el.data('resetText', $el[val]()) + + // push to event loop to allow forms to submit + setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + + if (state == 'loadingText') { + this.isLoading = true + $el.addClass(d).attr(d, d).prop(d, true) + } else if (this.isLoading) { + this.isLoading = false + $el.removeClass(d).removeAttr(d).prop(d, false) + } + }, this), 0) + } + + Button.prototype.toggle = function () { + var changed = true + var $parent = this.$element.closest('[data-toggle="buttons"]') + + if ($parent.length) { + var $input = this.$element.find('input') + if ($input.prop('type') == 'radio') { + if ($input.prop('checked')) changed = false + $parent.find('.active').removeClass('active') + this.$element.addClass('active') + } else if ($input.prop('type') == 'checkbox') { + if (($input.prop('checked')) !== this.$element.hasClass('active')) changed = false + this.$element.toggleClass('active') + } + $input.prop('checked', this.$element.hasClass('active')) + if (changed) $input.trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) + this.$element.toggleClass('active') + } + } + + + // BUTTON PLUGIN DEFINITION + // ======================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.button') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.button', (data = new Button(this, options))) + + if (option == 'toggle') data.toggle() + else if (option) data.setState(option) + }) + } + + var old = $.fn.button + + $.fn.button = Plugin + $.fn.button.Constructor = Button + + + // BUTTON NO CONFLICT + // ================== + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + + // BUTTON DATA-API + // =============== + + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target).closest('.btn') + Plugin.call($btn, 'toggle') + if (!($(e.target).is('input[type="radio"], input[type="checkbox"]'))) { + // Prevent double click on radios, and the double selections (so cancellation) on checkboxes + e.preventDefault() + // The target component still receive the focus + if ($btn.is('input,button')) $btn.trigger('focus') + else $btn.find('input:visible,button:visible').first().trigger('focus') + } + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: carousel.js v3.3.7 + * http://getbootstrap.com/javascript/#carousel + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // CAROUSEL CLASS DEFINITION + // ========================= + + var Carousel = function (element, options) { + this.$element = $(element) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options + this.paused = null + this.sliding = null + this.interval = null + this.$active = null + this.$items = null + + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element + .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) + .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) + } + + Carousel.VERSION = '3.3.7' + + Carousel.TRANSITION_DURATION = 600 + + Carousel.DEFAULTS = { + interval: 5000, + pause: 'hover', + wrap: true, + keyboard: true + } + + Carousel.prototype.keydown = function (e) { + if (/input|textarea/i.test(e.target.tagName)) return + switch (e.which) { + case 37: this.prev(); break + case 39: this.next(); break + default: return + } + + e.preventDefault() + } + + Carousel.prototype.cycle = function (e) { + e || (this.paused = false) + + this.interval && clearInterval(this.interval) + + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) + + return this + } + + Carousel.prototype.getItemIndex = function (item) { + this.$items = item.parent().children('.item') + return this.$items.index(item || this.$active) + } + + Carousel.prototype.getItemForDirection = function (direction, active) { + var activeIndex = this.getItemIndex(active) + var willWrap = (direction == 'prev' && activeIndex === 0) + || (direction == 'next' && activeIndex == (this.$items.length - 1)) + if (willWrap && !this.options.wrap) return active + var delta = direction == 'prev' ? -1 : 1 + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + + Carousel.prototype.to = function (pos) { + var that = this + var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) + + if (pos > (this.$items.length - 1) || pos < 0) return + + if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" + if (activeIndex == pos) return this.pause().cycle() + + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) + } + + Carousel.prototype.pause = function (e) { + e || (this.paused = true) + + if (this.$element.find('.next, .prev').length && $.support.transition) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } + + this.interval = clearInterval(this.interval) + + return this + } + + Carousel.prototype.next = function () { + if (this.sliding) return + return this.slide('next') + } + + Carousel.prototype.prev = function () { + if (this.sliding) return + return this.slide('prev') + } + + Carousel.prototype.slide = function (type, next) { + var $active = this.$element.find('.item.active') + var $next = next || this.getItemForDirection(type, $active) + var isCycling = this.interval + var direction = type == 'next' ? 'left' : 'right' + var that = this + + if ($next.hasClass('active')) return (this.sliding = false) + + var relatedTarget = $next[0] + var slideEvent = $.Event('slide.bs.carousel', { + relatedTarget: relatedTarget, + direction: direction + }) + this.$element.trigger(slideEvent) + if (slideEvent.isDefaultPrevented()) return + + this.sliding = true + + isCycling && this.pause() + + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) + $nextIndicator && $nextIndicator.addClass('active') + } + + var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" + if ($.support.transition && this.$element.hasClass('slide')) { + $next.addClass(type) + $next[0].offsetWidth // force reflow + $active.addClass(direction) + $next.addClass(direction) + $active + .one('bsTransitionEnd', function () { + $next.removeClass([type, direction].join(' ')).addClass('active') + $active.removeClass(['active', direction].join(' ')) + that.sliding = false + setTimeout(function () { + that.$element.trigger(slidEvent) + }, 0) + }) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) + } else { + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger(slidEvent) + } + + isCycling && this.cycle() + + return this + } + + + // CAROUSEL PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.carousel') + var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) + var action = typeof option == 'string' ? option : options.slide + + if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) + if (typeof option == 'number') data.to(option) + else if (action) data[action]() + else if (options.interval) data.pause().cycle() + }) + } + + var old = $.fn.carousel + + $.fn.carousel = Plugin + $.fn.carousel.Constructor = Carousel + + + // CAROUSEL NO CONFLICT + // ==================== + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + + + // CAROUSEL DATA-API + // ================= + + var clickHandler = function (e) { + var href + var $this = $(this) + var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 + if (!$target.hasClass('carousel')) return + var options = $.extend({}, $target.data(), $this.data()) + var slideIndex = $this.attr('data-slide-to') + if (slideIndex) options.interval = false + + Plugin.call($target, options) + + if (slideIndex) { + $target.data('bs.carousel').to(slideIndex) + } + + e.preventDefault() + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) + + $(window).on('load', function () { + $('[data-ride="carousel"]').each(function () { + var $carousel = $(this) + Plugin.call($carousel, $carousel.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: collapse.js v3.3.7 + * http://getbootstrap.com/javascript/#collapse + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + +/* jshint latedef: false */ + ++function ($) { + 'use strict'; + + // COLLAPSE PUBLIC CLASS DEFINITION + // ================================ + + var Collapse = function (element, options) { + this.$element = $(element) + this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + + '[data-toggle="collapse"][data-target="#' + element.id + '"]') + this.transitioning = null + + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + + if (this.options.toggle) this.toggle() + } + + Collapse.VERSION = '3.3.7' + + Collapse.TRANSITION_DURATION = 350 + + Collapse.DEFAULTS = { + toggle: true + } + + Collapse.prototype.dimension = function () { + var hasWidth = this.$element.hasClass('width') + return hasWidth ? 'width' : 'height' + } + + Collapse.prototype.show = function () { + if (this.transitioning || this.$element.hasClass('in')) return + + var activesData + var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + + var startEvent = $.Event('show.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + if (actives && actives.length) { + Plugin.call(actives, 'hide') + activesData || actives.data('bs.collapse', null) + } + + var dimension = this.dimension() + + this.$element + .removeClass('collapse') + .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) + + this.transitioning = 1 + + var complete = function () { + this.$element + .removeClass('collapsing') + .addClass('collapse in')[dimension]('') + this.transitioning = 0 + this.$element + .trigger('shown.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + var scrollSize = $.camelCase(['scroll', dimension].join('-')) + + this.$element + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) + } + + Collapse.prototype.hide = function () { + if (this.transitioning || !this.$element.hasClass('in')) return + + var startEvent = $.Event('hide.bs.collapse') + this.$element.trigger(startEvent) + if (startEvent.isDefaultPrevented()) return + + var dimension = this.dimension() + + this.$element[dimension](this.$element[dimension]())[0].offsetHeight + + this.$element + .addClass('collapsing') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) + + this.transitioning = 1 + + var complete = function () { + this.transitioning = 0 + this.$element + .removeClass('collapsing') + .addClass('collapse') + .trigger('hidden.bs.collapse') + } + + if (!$.support.transition) return complete.call(this) + + this.$element + [dimension](0) + .one('bsTransitionEnd', $.proxy(complete, this)) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) + } + + Collapse.prototype.toggle = function () { + this[this.$element.hasClass('in') ? 'hide' : 'show']() + } + + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + + + // COLLAPSE PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.collapse') + var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false + if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.collapse + + $.fn.collapse = Plugin + $.fn.collapse.Constructor = Collapse + + + // COLLAPSE NO CONFLICT + // ==================== + + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + // COLLAPSE DATA-API + // ================= + + $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { + var $this = $(this) + + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $this.data() + + Plugin.call($target, option) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: dropdown.js v3.3.7 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.3.7' + + function getParent($this) { + var selector = $this.attr('data-target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + var $parent = selector && $(selector) + + return $parent && $parent.length ? $parent : $this.parent() + } + + function clearMenus(e) { + if (e && e.which === 3) return + $(backdrop).remove() + $(toggle).each(function () { + var $this = $(this) + var $parent = getParent($this) + var relatedTarget = { relatedTarget: this } + + if (!$parent.hasClass('open')) return + + if (e && e.type == 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) return + + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') + $parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget)) + }) + } + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $(document.createElement('div')) + .addClass('dropdown-backdrop') + .insertAfter($(this)) + .on('click', clearMenus) + } + + var relatedTarget = { relatedTarget: this } + $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) + + if (e.isDefaultPrevented()) return + + $this + .trigger('focus') + .attr('aria-expanded', 'true') + + $parent + .toggleClass('open') + .trigger($.Event('shown.bs.dropdown', relatedTarget)) + } + + return false + } + + Dropdown.prototype.keydown = function (e) { + if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return + + var $this = $(this) + + e.preventDefault() + e.stopPropagation() + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + if (!isActive && e.which != 27 || isActive && e.which == 27) { + if (e.which == 27) $parent.find(toggle).trigger('focus') + return $this.trigger('click') + } + + var desc = ' li:not(.disabled):visible a' + var $items = $parent.find('.dropdown-menu' + desc) + + if (!$items.length) return + + var index = $items.index(e.target) + + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down + if (!~index) index = 0 + + $items.eq(index).trigger('focus') + } + + + // DROPDOWN PLUGIN DEFINITION + // ========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.dropdown') + + if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) + if (typeof option == 'string') data[option].call($this) + }) + } + + var old = $.fn.dropdown + + $.fn.dropdown = Plugin + $.fn.dropdown.Constructor = Dropdown + + + // DROPDOWN NO CONFLICT + // ==================== + + $.fn.dropdown.noConflict = function () { + $.fn.dropdown = old + return this + } + + + // APPLY TO STANDARD DROPDOWN ELEMENTS + // =================================== + + $(document) + .on('click.bs.dropdown.data-api', clearMenus) + .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) + .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: modal.js v3.3.7 + * http://getbootstrap.com/javascript/#modals + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // MODAL CLASS DEFINITION + // ====================== + + var Modal = function (element, options) { + this.options = options + this.$body = $(document.body) + this.$element = $(element) + this.$dialog = this.$element.find('.modal-dialog') + this.$backdrop = null + this.isShown = null + this.originalBodyPad = null + this.scrollbarWidth = 0 + this.ignoreBackdropClick = false + + if (this.options.remote) { + this.$element + .find('.modal-content') + .load(this.options.remote, $.proxy(function () { + this.$element.trigger('loaded.bs.modal') + }, this)) + } + } + + Modal.VERSION = '3.3.7' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 + + Modal.DEFAULTS = { + backdrop: true, + keyboard: true, + show: true + } + + Modal.prototype.toggle = function (_relatedTarget) { + return this.isShown ? this.hide() : this.show(_relatedTarget) + } + + Modal.prototype.show = function (_relatedTarget) { + var that = this + var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) + + this.$element.trigger(e) + + if (this.isShown || e.isDefaultPrevented()) return + + this.isShown = true + + this.checkScrollbar() + this.setScrollbar() + this.$body.addClass('modal-open') + + this.escape() + this.resize() + + this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) + + this.$dialog.on('mousedown.dismiss.bs.modal', function () { + that.$element.one('mouseup.dismiss.bs.modal', function (e) { + if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true + }) + }) + + this.backdrop(function () { + var transition = $.support.transition && that.$element.hasClass('fade') + + if (!that.$element.parent().length) { + that.$element.appendTo(that.$body) // don't move modals dom position + } + + that.$element + .show() + .scrollTop(0) + + that.adjustDialog() + + if (transition) { + that.$element[0].offsetWidth // force reflow + } + + that.$element.addClass('in') + + that.enforceFocus() + + var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) + + transition ? + that.$dialog // wait for modal to slide in + .one('bsTransitionEnd', function () { + that.$element.trigger('focus').trigger(e) + }) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + that.$element.trigger('focus').trigger(e) + }) + } + + Modal.prototype.hide = function (e) { + if (e) e.preventDefault() + + e = $.Event('hide.bs.modal') + + this.$element.trigger(e) + + if (!this.isShown || e.isDefaultPrevented()) return + + this.isShown = false + + this.escape() + this.resize() + + $(document).off('focusin.bs.modal') + + this.$element + .removeClass('in') + .off('click.dismiss.bs.modal') + .off('mouseup.dismiss.bs.modal') + + this.$dialog.off('mousedown.dismiss.bs.modal') + + $.support.transition && this.$element.hasClass('fade') ? + this.$element + .one('bsTransitionEnd', $.proxy(this.hideModal, this)) + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : + this.hideModal() + } + + Modal.prototype.enforceFocus = function () { + $(document) + .off('focusin.bs.modal') // guard against infinite focus loop + .on('focusin.bs.modal', $.proxy(function (e) { + if (document !== e.target && + this.$element[0] !== e.target && + !this.$element.has(e.target).length) { + this.$element.trigger('focus') + } + }, this)) + } + + Modal.prototype.escape = function () { + if (this.isShown && this.options.keyboard) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { + e.which == 27 && this.hide() + }, this)) + } else if (!this.isShown) { + this.$element.off('keydown.dismiss.bs.modal') + } + } + + Modal.prototype.resize = function () { + if (this.isShown) { + $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) + } else { + $(window).off('resize.bs.modal') + } + } + + Modal.prototype.hideModal = function () { + var that = this + this.$element.hide() + this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetAdjustments() + that.resetScrollbar() + that.$element.trigger('hidden.bs.modal') + }) + } + + Modal.prototype.removeBackdrop = function () { + this.$backdrop && this.$backdrop.remove() + this.$backdrop = null + } + + Modal.prototype.backdrop = function (callback) { + var that = this + var animate = this.$element.hasClass('fade') ? 'fade' : '' + + if (this.isShown && this.options.backdrop) { + var doAnimate = $.support.transition && animate + + this.$backdrop = $(document.createElement('div')) + .addClass('modal-backdrop ' + animate) + .appendTo(this.$body) + + this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { + if (this.ignoreBackdropClick) { + this.ignoreBackdropClick = false + return + } + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus() + : this.hide() + }, this)) + + if (doAnimate) this.$backdrop[0].offsetWidth // force reflow + + this.$backdrop.addClass('in') + + if (!callback) return + + doAnimate ? + this.$backdrop + .one('bsTransitionEnd', callback) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callback() + + } else if (!this.isShown && this.$backdrop) { + this.$backdrop.removeClass('in') + + var callbackRemove = function () { + that.removeBackdrop() + callback && callback() + } + $.support.transition && this.$element.hasClass('fade') ? + this.$backdrop + .one('bsTransitionEnd', callbackRemove) + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : + callbackRemove() + + } else if (callback) { + callback() + } + } + + // these following methods are used to handle overflowing modals + + Modal.prototype.handleUpdate = function () { + this.adjustDialog() + } + + Modal.prototype.adjustDialog = function () { + var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight + + this.$element.css({ + paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', + paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' + }) + } + + Modal.prototype.resetAdjustments = function () { + this.$element.css({ + paddingLeft: '', + paddingRight: '' + }) + } + + Modal.prototype.checkScrollbar = function () { + var fullWindowWidth = window.innerWidth + if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 + var documentElementRect = document.documentElement.getBoundingClientRect() + fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) + } + this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth + this.scrollbarWidth = this.measureScrollbar() + } + + Modal.prototype.setScrollbar = function () { + var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) + this.originalBodyPad = document.body.style.paddingRight || '' + if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) + } + + Modal.prototype.resetScrollbar = function () { + this.$body.css('padding-right', this.originalBodyPad) + } + + Modal.prototype.measureScrollbar = function () { // thx walsh + var scrollDiv = document.createElement('div') + scrollDiv.className = 'modal-scrollbar-measure' + this.$body.append(scrollDiv) + var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + this.$body[0].removeChild(scrollDiv) + return scrollbarWidth + } + + + // MODAL PLUGIN DEFINITION + // ======================= + + function Plugin(option, _relatedTarget) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.modal') + var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) + + if (!data) $this.data('bs.modal', (data = new Modal(this, options))) + if (typeof option == 'string') data[option](_relatedTarget) + else if (options.show) data.show(_relatedTarget) + }) + } + + var old = $.fn.modal + + $.fn.modal = Plugin + $.fn.modal.Constructor = Modal + + + // MODAL NO CONFLICT + // ================= + + $.fn.modal.noConflict = function () { + $.fn.modal = old + return this + } + + + // MODAL DATA-API + // ============== + + $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { + var $this = $(this) + var href = $this.attr('href') + var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 + var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) + + if ($this.is('a')) e.preventDefault() + + $target.one('show.bs.modal', function (showEvent) { + if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown + $target.one('hidden.bs.modal', function () { + $this.is(':visible') && $this.trigger('focus') + }) + }) + Plugin.call($target, option, this) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tooltip.js v3.3.7 + * http://getbootstrap.com/javascript/#tooltip + * Inspired by the original jQuery.tipsy by Jason Frame + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TOOLTIP PUBLIC CLASS DEFINITION + // =============================== + + var Tooltip = function (element, options) { + this.type = null + this.options = null + this.enabled = null + this.timeout = null + this.hoverState = null + this.$element = null + this.inState = null + + this.init('tooltip', element, options) + } + + Tooltip.VERSION = '3.3.7' + + Tooltip.TRANSITION_DURATION = 150 + + Tooltip.DEFAULTS = { + animation: true, + placement: 'top', + selector: false, + template: '', + trigger: 'hover focus', + title: '', + delay: 0, + html: false, + container: false, + viewport: { + selector: 'body', + padding: 0 + } + } + + Tooltip.prototype.init = function (type, element, options) { + this.enabled = true + this.type = type + this.$element = $(element) + this.options = this.getOptions(options) + this.$viewport = this.options.viewport && $($.isFunction(this.options.viewport) ? this.options.viewport.call(this, this.$element) : (this.options.viewport.selector || this.options.viewport)) + this.inState = { click: false, hover: false, focus: false } + + if (this.$element[0] instanceof document.constructor && !this.options.selector) { + throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') + } + + var triggers = this.options.trigger.split(' ') + + for (var i = triggers.length; i--;) { + var trigger = triggers[i] + + if (trigger == 'click') { + this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) + } else if (trigger != 'manual') { + var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' + var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' + + this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) + this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) + } + } + + this.options.selector ? + (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : + this.fixTitle() + } + + Tooltip.prototype.getDefaults = function () { + return Tooltip.DEFAULTS + } + + Tooltip.prototype.getOptions = function (options) { + options = $.extend({}, this.getDefaults(), this.$element.data(), options) + + if (options.delay && typeof options.delay == 'number') { + options.delay = { + show: options.delay, + hide: options.delay + } + } + + return options + } + + Tooltip.prototype.getDelegateOptions = function () { + var options = {} + var defaults = this.getDefaults() + + this._options && $.each(this._options, function (key, value) { + if (defaults[key] != value) options[key] = value + }) + + return options + } + + Tooltip.prototype.enter = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusin' ? 'focus' : 'hover'] = true + } + + if (self.tip().hasClass('in') || self.hoverState == 'in') { + self.hoverState = 'in' + return + } + + clearTimeout(self.timeout) + + self.hoverState = 'in' + + if (!self.options.delay || !self.options.delay.show) return self.show() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'in') self.show() + }, self.options.delay.show) + } + + Tooltip.prototype.isInStateTrue = function () { + for (var key in this.inState) { + if (this.inState[key]) return true + } + + return false + } + + Tooltip.prototype.leave = function (obj) { + var self = obj instanceof this.constructor ? + obj : $(obj.currentTarget).data('bs.' + this.type) + + if (!self) { + self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) + $(obj.currentTarget).data('bs.' + this.type, self) + } + + if (obj instanceof $.Event) { + self.inState[obj.type == 'focusout' ? 'focus' : 'hover'] = false + } + + if (self.isInStateTrue()) return + + clearTimeout(self.timeout) + + self.hoverState = 'out' + + if (!self.options.delay || !self.options.delay.hide) return self.hide() + + self.timeout = setTimeout(function () { + if (self.hoverState == 'out') self.hide() + }, self.options.delay.hide) + } + + Tooltip.prototype.show = function () { + var e = $.Event('show.bs.' + this.type) + + if (this.hasContent() && this.enabled) { + this.$element.trigger(e) + + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) + if (e.isDefaultPrevented() || !inDom) return + var that = this + + var $tip = this.tip() + + var tipId = this.getUID(this.type) + + this.setContent() + $tip.attr('id', tipId) + this.$element.attr('aria-describedby', tipId) + + if (this.options.animation) $tip.addClass('fade') + + var placement = typeof this.options.placement == 'function' ? + this.options.placement.call(this, $tip[0], this.$element[0]) : + this.options.placement + + var autoToken = /\s?auto?\s?/i + var autoPlace = autoToken.test(placement) + if (autoPlace) placement = placement.replace(autoToken, '') || 'top' + + $tip + .detach() + .css({ top: 0, left: 0, display: 'block' }) + .addClass(placement) + .data('bs.' + this.type, this) + + this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) + this.$element.trigger('inserted.bs.' + this.type) + + var pos = this.getPosition() + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (autoPlace) { + var orgPlacement = placement + var viewportDim = this.getPosition(this.$viewport) + + placement = placement == 'bottom' && pos.bottom + actualHeight > viewportDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < viewportDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > viewportDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < viewportDim.left ? 'right' : + placement + + $tip + .removeClass(orgPlacement) + .addClass(placement) + } + + var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) + + this.applyPlacement(calculatedOffset, placement) + + var complete = function () { + var prevHoverState = that.hoverState + that.$element.trigger('shown.bs.' + that.type) + that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) + } + + $.support.transition && this.$tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + } + } + + Tooltip.prototype.applyPlacement = function (offset, placement) { + var $tip = this.tip() + var width = $tip[0].offsetWidth + var height = $tip[0].offsetHeight + + // manually read margins because getBoundingClientRect includes difference + var marginTop = parseInt($tip.css('margin-top'), 10) + var marginLeft = parseInt($tip.css('margin-left'), 10) + + // we must check for NaN for ie 8/9 + if (isNaN(marginTop)) marginTop = 0 + if (isNaN(marginLeft)) marginLeft = 0 + + offset.top += marginTop + offset.left += marginLeft + + // $.fn.offset doesn't round pixel values + // so we use setOffset directly with our own function B-0 + $.offset.setOffset($tip[0], $.extend({ + using: function (props) { + $tip.css({ + top: Math.round(props.top), + left: Math.round(props.left) + }) + } + }, offset), 0) + + $tip.addClass('in') + + // check to see if placing tip in new offset caused the tip to resize itself + var actualWidth = $tip[0].offsetWidth + var actualHeight = $tip[0].offsetHeight + + if (placement == 'top' && actualHeight != height) { + offset.top = offset.top + height - actualHeight + } + + var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) + + if (delta.left) offset.left += delta.left + else offset.top += delta.top + + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' + + $tip.offset(offset) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) + } + + Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { + this.arrow() + .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isVertical ? 'top' : 'left', '') + } + + Tooltip.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + + $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) + $tip.removeClass('fade in top bottom left right') + } + + Tooltip.prototype.hide = function (callback) { + var that = this + var $tip = $(this.$tip) + var e = $.Event('hide.bs.' + this.type) + + function complete() { + if (that.hoverState != 'in') $tip.detach() + if (that.$element) { // TODO: Check whether guarding this code with this `if` is really necessary. + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + } + callback && callback() + } + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + $tip.removeClass('in') + + $.support.transition && $tip.hasClass('fade') ? + $tip + .one('bsTransitionEnd', complete) + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : + complete() + + this.hoverState = null + + return this + } + + Tooltip.prototype.fixTitle = function () { + var $e = this.$element + if ($e.attr('title') || typeof $e.attr('data-original-title') != 'string') { + $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') + } + } + + Tooltip.prototype.hasContent = function () { + return this.getTitle() + } + + Tooltip.prototype.getPosition = function ($element) { + $element = $element || this.$element + + var el = $element[0] + var isBody = el.tagName == 'BODY' + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var isSvg = window.SVGElement && el instanceof window.SVGElement + // Avoid using $.offset() on SVGs since it gives incorrect results in jQuery 3. + // See https://github.com/twbs/bootstrap/issues/20280 + var elOffset = isBody ? { top: 0, left: 0 } : (isSvg ? null : $element.offset()) + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) + } + + Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { + return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : + placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : + /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } + + } + + Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { + var delta = { top: 0, left: 0 } + if (!this.$viewport) return delta + + var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 + var viewportDimensions = this.getPosition(this.$viewport) + + if (/right|left/.test(placement)) { + var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll + var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight + if (topEdgeOffset < viewportDimensions.top) { // top overflow + delta.top = viewportDimensions.top - topEdgeOffset + } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow + delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset + } + } else { + var leftEdgeOffset = pos.left - viewportPadding + var rightEdgeOffset = pos.left + viewportPadding + actualWidth + if (leftEdgeOffset < viewportDimensions.left) { // left overflow + delta.left = viewportDimensions.left - leftEdgeOffset + } else if (rightEdgeOffset > viewportDimensions.right) { // right overflow + delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset + } + } + + return delta + } + + Tooltip.prototype.getTitle = function () { + var title + var $e = this.$element + var o = this.options + + title = $e.attr('data-original-title') + || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) + + return title + } + + Tooltip.prototype.getUID = function (prefix) { + do prefix += ~~(Math.random() * 1000000) + while (document.getElementById(prefix)) + return prefix + } + + Tooltip.prototype.tip = function () { + if (!this.$tip) { + this.$tip = $(this.options.template) + if (this.$tip.length != 1) { + throw new Error(this.type + ' `template` option must consist of exactly 1 top-level element!') + } + } + return this.$tip + } + + Tooltip.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) + } + + Tooltip.prototype.enable = function () { + this.enabled = true + } + + Tooltip.prototype.disable = function () { + this.enabled = false + } + + Tooltip.prototype.toggleEnabled = function () { + this.enabled = !this.enabled + } + + Tooltip.prototype.toggle = function (e) { + var self = this + if (e) { + self = $(e.currentTarget).data('bs.' + this.type) + if (!self) { + self = new this.constructor(e.currentTarget, this.getDelegateOptions()) + $(e.currentTarget).data('bs.' + this.type, self) + } + } + + if (e) { + self.inState.click = !self.inState.click + if (self.isInStateTrue()) self.enter(self) + else self.leave(self) + } else { + self.tip().hasClass('in') ? self.leave(self) : self.enter(self) + } + } + + Tooltip.prototype.destroy = function () { + var that = this + clearTimeout(this.timeout) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + if (that.$tip) { + that.$tip.detach() + } + that.$tip = null + that.$arrow = null + that.$viewport = null + that.$element = null + }) + } + + + // TOOLTIP PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tooltip + + $.fn.tooltip = Plugin + $.fn.tooltip.Constructor = Tooltip + + + // TOOLTIP NO CONFLICT + // =================== + + $.fn.tooltip.noConflict = function () { + $.fn.tooltip = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: popover.js v3.3.7 + * http://getbootstrap.com/javascript/#popovers + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // POPOVER PUBLIC CLASS DEFINITION + // =============================== + + var Popover = function (element, options) { + this.init('popover', element, options) + } + + if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') + + Popover.VERSION = '3.3.7' + + Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { + placement: 'right', + trigger: 'click', + content: '', + template: '' + }) + + + // NOTE: POPOVER EXTENDS tooltip.js + // ================================ + + Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) + + Popover.prototype.constructor = Popover + + Popover.prototype.getDefaults = function () { + return Popover.DEFAULTS + } + + Popover.prototype.setContent = function () { + var $tip = this.tip() + var title = this.getTitle() + var content = this.getContent() + + $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events + this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' + ](content) + + $tip.removeClass('fade top bottom left right in') + + // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do + // this manually by checking the contents. + if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() + } + + Popover.prototype.hasContent = function () { + return this.getTitle() || this.getContent() + } + + Popover.prototype.getContent = function () { + var $e = this.$element + var o = this.options + + return $e.attr('data-content') + || (typeof o.content == 'function' ? + o.content.call($e[0]) : + o.content) + } + + Popover.prototype.arrow = function () { + return (this.$arrow = this.$arrow || this.tip().find('.arrow')) + } + + + // POPOVER PLUGIN DEFINITION + // ========================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + + if (!data && /destroy|hide/.test(option)) return + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.popover + + $.fn.popover = Plugin + $.fn.popover.Constructor = Popover + + + // POPOVER NO CONFLICT + // =================== + + $.fn.popover.noConflict = function () { + $.fn.popover = old + return this + } + +}(jQuery); + +/* ======================================================================== + * Bootstrap: scrollspy.js v3.3.7 + * http://getbootstrap.com/javascript/#scrollspy + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // SCROLLSPY CLASS DEFINITION + // ========================== + + function ScrollSpy(element, options) { + this.$body = $(document.body) + this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) + this.options = $.extend({}, ScrollSpy.DEFAULTS, options) + this.selector = (this.options.target || '') + ' .nav li > a' + this.offsets = [] + this.targets = [] + this.activeTarget = null + this.scrollHeight = 0 + + this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) + this.refresh() + this.process() + } + + ScrollSpy.VERSION = '3.3.7' + + ScrollSpy.DEFAULTS = { + offset: 10 + } + + ScrollSpy.prototype.getScrollHeight = function () { + return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) + } + + ScrollSpy.prototype.refresh = function () { + var that = this + var offsetMethod = 'offset' + var offsetBase = 0 + + this.offsets = [] + this.targets = [] + this.scrollHeight = this.getScrollHeight() + + if (!$.isWindow(this.$scrollElement[0])) { + offsetMethod = 'position' + offsetBase = this.$scrollElement.scrollTop() + } + + this.$body + .find(this.selector) + .map(function () { + var $el = $(this) + var href = $el.data('target') || $el.attr('href') + var $href = /^#./.test(href) && $(href) + + return ($href + && $href.length + && $href.is(':visible') + && [[$href[offsetMethod]().top + offsetBase, href]]) || null + }) + .sort(function (a, b) { return a[0] - b[0] }) + .each(function () { + that.offsets.push(this[0]) + that.targets.push(this[1]) + }) + } + + ScrollSpy.prototype.process = function () { + var scrollTop = this.$scrollElement.scrollTop() + this.options.offset + var scrollHeight = this.getScrollHeight() + var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() + var offsets = this.offsets + var targets = this.targets + var activeTarget = this.activeTarget + var i + + if (this.scrollHeight != scrollHeight) { + this.refresh() + } + + if (scrollTop >= maxScroll) { + return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) + } + + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() + } + + for (i = offsets.length; i--;) { + activeTarget != targets[i] + && scrollTop >= offsets[i] + && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) + && this.activate(targets[i]) + } + } + + ScrollSpy.prototype.activate = function (target) { + this.activeTarget = target + + this.clear() + + var selector = this.selector + + '[data-target="' + target + '"],' + + this.selector + '[href="' + target + '"]' + + var active = $(selector) + .parents('li') + .addClass('active') + + if (active.parent('.dropdown-menu').length) { + active = active + .closest('li.dropdown') + .addClass('active') + } + + active.trigger('activate.bs.scrollspy') + } + + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + + + // SCROLLSPY PLUGIN DEFINITION + // =========================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.scrollspy') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.scrollspy + + $.fn.scrollspy = Plugin + $.fn.scrollspy.Constructor = ScrollSpy + + + // SCROLLSPY NO CONFLICT + // ===================== + + $.fn.scrollspy.noConflict = function () { + $.fn.scrollspy = old + return this + } + + + // SCROLLSPY DATA-API + // ================== + + $(window).on('load.bs.scrollspy.data-api', function () { + $('[data-spy="scroll"]').each(function () { + var $spy = $(this) + Plugin.call($spy, $spy.data()) + }) + }) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: tab.js v3.3.7 + * http://getbootstrap.com/javascript/#tabs + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // TAB CLASS DEFINITION + // ==================== + + var Tab = function (element) { + // jscs:disable requireDollarBeforejQueryAssignment + this.element = $(element) + // jscs:enable requireDollarBeforejQueryAssignment + } + + Tab.VERSION = '3.3.7' + + Tab.TRANSITION_DURATION = 150 + + Tab.prototype.show = function () { + var $this = this.element + var $ul = $this.closest('ul:not(.dropdown-menu)') + var selector = $this.data('target') + + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 + } + + if ($this.parent('li').hasClass('active')) return + + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] + }) + + $previous.trigger(hideEvent) + $this.trigger(showEvent) + + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return + + var $target = $(selector) + + this.activate($this.closest('li'), $ul) + this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) + $this.trigger({ + type: 'shown.bs.tab', + relatedTarget: $previous[0] + }) + }) + } + + Tab.prototype.activate = function (element, container, callback) { + var $active = container.find('> .active') + var transition = callback + && $.support.transition + && ($active.length && $active.hasClass('fade') || !!container.find('> .fade').length) + + function next() { + $active + .removeClass('active') + .find('> .dropdown-menu > .active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) + + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + + if (transition) { + element[0].offsetWidth // reflow for transition + element.addClass('in') + } else { + element.removeClass('fade') + } + + if (element.parent('.dropdown-menu').length) { + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) + } + + callback && callback() + } + + $active.length && transition ? + $active + .one('bsTransitionEnd', next) + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : + next() + + $active.removeClass('in') + } + + + // TAB PLUGIN DEFINITION + // ===================== + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.tab') + + if (!data) $this.data('bs.tab', (data = new Tab(this))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.tab + + $.fn.tab = Plugin + $.fn.tab.Constructor = Tab + + + // TAB NO CONFLICT + // =============== + + $.fn.tab.noConflict = function () { + $.fn.tab = old + return this + } + + + // TAB DATA-API + // ============ + + var clickHandler = function (e) { + e.preventDefault() + Plugin.call($(this), 'show') + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) + +}(jQuery); + +/* ======================================================================== + * Bootstrap: affix.js v3.3.7 + * http://getbootstrap.com/javascript/#affix + * ======================================================================== + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + + ++function ($) { + 'use strict'; + + // AFFIX CLASS DEFINITION + // ====================== + + var Affix = function (element, options) { + this.options = $.extend({}, Affix.DEFAULTS, options) + + this.$target = $(this.options.target) + .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) + .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) + + this.$element = $(element) + this.affixed = null + this.unpin = null + this.pinnedOffset = null + + this.checkPosition() + } + + Affix.VERSION = '3.3.7' + + Affix.RESET = 'affix affix-top affix-bottom' + + Affix.DEFAULTS = { + offset: 0, + target: window + } + + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && scrollTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + + Affix.prototype.getPinnedOffset = function () { + if (this.pinnedOffset) return this.pinnedOffset + this.$element.removeClass(Affix.RESET).addClass('affix') + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + return (this.pinnedOffset = position.top - scrollTop) + } + + Affix.prototype.checkPositionWithEventLoop = function () { + setTimeout($.proxy(this.checkPosition, this), 1) + } + + Affix.prototype.checkPosition = function () { + if (!this.$element.is(':visible')) return + + var height = this.$element.height() + var offset = this.options.offset + var offsetTop = offset.top + var offsetBottom = offset.bottom + var scrollHeight = Math.max($(document).height(), $(document.body).height()) + + if (typeof offset != 'object') offsetBottom = offsetTop = offset + if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) + if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) + + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) + + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') + + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') + + this.$element.trigger(e) + + if (e.isDefaultPrevented()) return + + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } + + if (affix == 'bottom') { + this.$element.offset({ + top: scrollHeight - height - offsetBottom + }) + } + } + + + // AFFIX PLUGIN DEFINITION + // ======================= + + function Plugin(option) { + return this.each(function () { + var $this = $(this) + var data = $this.data('bs.affix') + var options = typeof option == 'object' && option + + if (!data) $this.data('bs.affix', (data = new Affix(this, options))) + if (typeof option == 'string') data[option]() + }) + } + + var old = $.fn.affix + + $.fn.affix = Plugin + $.fn.affix.Constructor = Affix + + + // AFFIX NO CONFLICT + // ================= + + $.fn.affix.noConflict = function () { + $.fn.affix = old + return this + } + + + // AFFIX DATA-API + // ============== + + $(window).on('load', function () { + $('[data-spy="affix"]').each(function () { + var $spy = $(this) + var data = $spy.data() + + data.offset = data.offset || {} + + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop + + Plugin.call($spy, data) + }) + }) + +}(jQuery); diff --git a/public/vendor/bootstrap/js/bootstrap.min.js b/public/vendor/bootstrap/js/bootstrap.min.js deleted file mode 100644 index e79c065134..0000000000 --- a/public/vendor/bootstrap/js/bootstrap.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * Bootstrap v3.3.6 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under the MIT license - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1||b[0]>2)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher, but lower than version 3")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.6",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.6",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.6",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.6",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger(a.Event("hidden.bs.dropdown",f)))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.6",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger(a.Event("shown.bs.dropdown",h))}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.6",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.6",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.6",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); \ No newline at end of file diff --git a/public/vendor/jquery/css/smoothness/images/animated-overlay.gif b/public/vendor/jquery/css/smoothness/images/animated-overlay.gif deleted file mode 100644 index d441f75ebfbdf26a265dfccd670120d25c0a341c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1738 zcmZ|OX;ji_6b5ixNYt8>l?gOuO)6lU%W(mxn(`>1S(XO;u`D+P%xqBvMr|w-Vyr1s z7R|Cn0b8|Hu<=Zmv1mFqh9Fj!NuZfKB2MP$e75`XJ@>=!y!Ux9xR3x;EW!q1^V>X| znVFuRUN`NqJ2)ybXh%e__h!!pv(M|S3+?9F%(K}zyE40MGyhWF5-IDgL&=%2-9`Nk z!1@8uk4t%_{(K~>N;sK&dzJbwJ=$kYTlL=$%#0Pfh>U{%i@~wWbvYsD_K-D`&+u1( z#Ma`>%q<^UhzGvi(hyE`zCD{-=2|zL5>wnB=DE!U?(CZG%q4@lDnCq_%&3DCla#(X zmBhDD+RN$aMWWHm?ig*>1Onn6~r?Ma~N2JKAxN>H%UtRyRqS)6Um!-Tz%-r=& zQmTb^JFIe3W^-kAm`}`2P|niMh>RYyd)S^f(dbrx965?rzbhP|XeP}o&&DSZ4|oYQ z)I{f!SfycYw?3=9W;o-B%U5xs(pP267X~9-7L|4WzaYexC0GtG8wWygm63rF{llCEraxzkc=IxvFQ-y37=_;e5 zJLq^gsSO0Ayz?a>E_?{dmUc+t#qv$)XN8$<<}rQ#)lsiw+pmL&J>~+hgpo>i$m+;l zZIa_ZRIfSeT$~v5d`EBV&*k`apPgjv&B|+d`Q!nyu{L4rs%ZfoF0*Kq8I%ByOcFpL zK=>wzofZo<+0GZLCnWM3oQ^pb(gRSf02;~cEn@LJ>~XB9IkEX{$N#Z`m%>S!U{uPx zloI%bLdo$Adxlh(Uv^yX7s5G&C zLwNRG>~T?G{kzupp8EcyLGPoPf)@&9Wqfw_l&uU-6cexk%5;uQg%wb=0k_733{i#& z1a2p)gV3S2+QG1-K9tZ}E~I<(P0r2aFFY-c{o?TUOz3Xjod#TLE2A_c?*T7t z=1>~%YW450{Qqno4t`}gvLnuMrcu8+#xEBoY%2_+Mb#Z6S38+r*M4O`-+!zl(@m`D zQsi|GA2l3gEy}LFe<#Hv8?$_L#u8E|3-bP$*La*E>B{X!Sy4i6?TKam!49aXCAW4S*P_O^H4^*DpiA40o}Uqw~Eo&veh1`|8i zD2$x+>_b^bXE4N;AW=5>iYak2%!JAh0j1*k1{p#iRCjbB7!cSws~U{1IA@acLII$t z$>X#A+^s6iJ5~DFG!xa?>z{=lxtdi1rzbM-(nqAu3D8h-&64xo6|E!p?pK0xT;qoK z`6%+SpBk+~M?nO}>2mTw!A{yZ6O>Z@kwSd4;8aWU5z!P~tQl?u==^+R`{OmOS}oZh zOXQ3{6kuz?Is^n^L7;9ieB9C+8B{>t+pDrlq4xGDDn#T#3T5$l1g`FTQkU;b-981j zNm{zC`$wn7etklM#qHI4=3m5gwa6DNS{?Z!vSObi_od{4eUo=_S2BKNpkSdiqe(k9WtkeM79;2-%CFbb)aB=&H1?i1}uwFzoZQ(38Kn1zBP ORn*B%u*Wk|4g3!*Rv{Mv diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png b/public/vendor/jquery/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png deleted file mode 100644 index 42319a6877d6e3741a652c00e52f31cf9ffd219c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 212 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F1SA+{?>A)!QcOwS?k)_>#w|r1Kptm-M`SUO z_5fqIli7AahM1>|V~EA+ zRdP`(kYX@0Ff`FMFwr$O2r)FYGBLF>G|)9Lw=yu`O+UB{MMG|WN@iLmZVfe7l~O?)F zK#IZ0z|ch3z(m*BAjHtn%EZLV)LhrV+{(aUvB#`8C>nC}Q!>*kacj6FsuTd!z~JfX K=d#Wzp$P!<&on>) diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png b/public/vendor/jquery/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png index 9ed29e7f7f2fed091802622c34d00a5139f25a77..3e87efd7e9c8ea4d5475753108b379216a061010 100644 GIT binary patch delta 94 zcmX@lbe?I#k8m?x0}EXvvk*fID-$CtBV%m?11kfAw#?kM3=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1HGDU@DhSlT;OXk;vd$@?2>=EC8Z7_- delta 94 zcmX@lbe?I#k8l%R0~1|igAhYQD-%;I0}EXPb1MUbPfGpq3=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1H8j_oKL%=G@O1TaS?83{1ONfa8Y=(* diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png b/public/vendor/jquery/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png index f0e86f1a765581a7ed4b724c79e8503e462654cc..e1602e3b8b7cf81a60c312e85baabb3ec16df03b 100644 GIT binary patch delta 94 zcmX@lc%E@WMYx%+frYM-S%{&9m5Gs+k&(86ft7)Qf?S#;0|SFRc?Mt dW?ChKk%1w04S~-0mI5^}c)I$ztaD0e0sw@g7-Ikc delta 94 zcmX@lc%E@WMYxHsfr+lML5QKDm5GU!sj;qsxs`!IIQzc;3=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1HK;|gIRiB?c)I$ztaD0e0szI77;OLm diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png b/public/vendor/jquery/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png index 15d191d79b0ddce6750b6e97f662d7778cc419f3..278b8b23fe119c97b828f220ed3720540fe4d559 100644 GIT binary patch delta 94 zcmZo;YGazPKHN;#z(Uu^EX2^l%EZXZ$XMIJz{Rc?Mt dW?ChKk%1w04c|?!3Ia7Sc)I$ztaD0e0syyz8BzcM delta 94 zcmZo;YGazPKHNmtz(m*BAjHtn%EZ(P$Tl#yGB7Z0daBL9z@S><8c~vxSdwa$o1c=I bR>@#wV2E9V%l+8tKn)C@u6{1-oD!M_fq_A_#5JNMC9x#cDmOnR dGp&-r$iNW0hCt_gOMw~~JYD@<);T3K0RUrg7zzLY delta 94 zcmZo;YGazPKHNmtz(m*BAjHtn%EZLV)LhrV+{(aUvB#`83=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1HCz%^3IJ+g@O1TaS?83{1OUX@83F(R diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png b/public/vendor/jquery/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png index 31b9eb03dd4f6daeb4f1c4628be656a62d2b3f20..443633de42549092602a51a9a47fff90838ee297 100644 GIT binary patch delta 76 zcmX@ZbcSidS1B`H0}EXvvk*fID-$CtBV%m?11kfAw#?kMlld9N)v!x{H@Pav00f?{ KelF{r5}E)VToft* delta 76 zcmX@ZbcSidS1A)+0~1|igAhYQD-%;I17lqSb1MUb530A9PUdG6SHmvdSdlEr00f?{ KelF{r5}E)hh!hF{ diff --git a/public/vendor/jquery/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/public/vendor/jquery/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png index 75831b53483643a7e6e595b4b259fdbd6efa045a..cd72b628dcfb922f1c08587b0389224783f19485 100644 GIT binary patch delta 94 zcmbQiG=piv!EiHO0}EXvvk*fID-$CtBV%m?11kfAw#?kM3=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1HGDU@DhSlT;OXk;vd$@?2>{C38HfM? delta 94 zcmbQiG=piv!Eh5@0~1|igAhYQD-#ndQwv=Kb1MUbP=;5m3=9maC9V-ADTyViR=N2p dnQ4^_Mh1r1HK@b}?F4FI@O1TaS?83{1OSiA7(f63 diff --git a/public/vendor/jquery/css/smoothness/images/ui-icons_222222_256x240.png b/public/vendor/jquery/css/smoothness/images/ui-icons_222222_256x240.png index c1cb1170c8b3795835b8831ab81fa9ae63b606b1..e723e17cb5428094de75a0d3c6f4f3c07dbd3bbe 100644 GIT binary patch delta 87 zcmeA&>oVKWC*^IXYhbQxXdGf_U}a=zWooHyU|?lnAkEm>&A`B*TH+c}l9E`GYL%Ox gl9^V?U}Rtj(_mm}Wn^Y$Y68*l`RoFL$v3480s1)@6aWAK delta 87 zcmeA&>oVKWC*^IdYha{nU>IUxYGrI}WoV#lU}0ropqX%B5(5K+YKdz^NlIc#s#R`& XN@iLmgOPzDb`24>6?&6zN*MwG5_A`I diff --git a/public/vendor/jquery/css/smoothness/images/ui-icons_2e83ff_256x240.png b/public/vendor/jquery/css/smoothness/images/ui-icons_2e83ff_256x240.png index 84b601bf0f726bf95801da487deaf2344a32e4b8..1f5f49756ca64bd20a1048bd7a3a584457d4bf00 100644 GIT binary patch delta 87 zcmX@Ad{lXZr=Yi)u7SC(p>c?zft8V=m8qq+fq|8Qfiz=hHvCa~u2uzljHUt2%ITI@Y delta 69 zcmca$cEN1J6)7oWT>~Rs1H%vlQ!8U*D?g3lP15D62~DOVOya$Szg)@ E0KMH3ivR!s diff --git a/public/vendor/jquery/css/smoothness/images/ui-icons_888888_256x240.png b/public/vendor/jquery/css/smoothness/images/ui-icons_888888_256x240.png index feea0e20264c4649b2ef03fe6705d69b4937c04e..ee5e33f27235d1b5cf0259f63f3894a6be33c679 100644 GIT binary patch delta 87 zcmca^cHL~lZ7FXvT?2DnL*o!b11lp#D^p8t0|P4q18K(2ZUzPh)e_f;l9a@fRIA+l hl+3hB1|tJQm<9t&D~Rs1H%vlQ!8U*D?g3lNcBnR7+eVN>UO_Qmu0H XQ!>*k8H@}Jv1^F1tc?zft8V=m8qq+fq|8Qfiz=hHv=0)&&i(t,!s)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(i,n){function s(t,i,n,s){return e.each(a,function(){i-=parseFloat(e.css(t,"padding"+this))||0,n&&(i-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(i-=parseFloat(e.css(t,"margin"+this))||0)}),i}var a="Width"===n?["Left","Right"]:["Top","Bottom"],o=n.toLowerCase(),r={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+n]=function(i){return i===t?r["inner"+n].call(this):this.each(function(){e(this).css(o,s(this,i)+"px")})},e.fn["outer"+n]=function(t,i){return"number"!=typeof t?r["outer"+n].call(this,t):this.each(function(){e(this).css(o,s(this,t,!0,i)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(i){return arguments.length?t.call(this,e.camelCase(i)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.support.selectstart="onselectstart"in document.createElement("div"),e.fn.extend({disableSelection:function(){return this.bind((e.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}}),e.extend(e.ui,{plugin:{add:function(t,i,n){var s,a=e.ui[t].prototype;for(s in n)a.plugins[s]=a.plugins[s]||[],a.plugins[s].push([i,n[s]])},call:function(e,t,i){var n,s=e.plugins[t];if(s&&e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType)for(n=0;s.length>n;n++)e.options[s[n][0]]&&s[n][1].apply(e.element,i)}},hasScroll:function(t,i){if("hidden"===e(t).css("overflow"))return!1;var n=i&&"left"===i?"scrollLeft":"scrollTop",s=!1;return t[n]>0?!0:(t[n]=1,s=t[n]>0,t[n]=0,s)}})})(jQuery);(function(t,e){var i=0,s=Array.prototype.slice,n=t.cleanData;t.cleanData=function(e){for(var i,s=0;null!=(i=e[s]);s++)try{t(i).triggerHandler("remove")}catch(o){}n(e)},t.widget=function(i,s,n){var o,a,r,h,l={},c=i.split(".")[0];i=i.split(".")[1],o=c+"-"+i,n||(n=s,s=t.Widget),t.expr[":"][o.toLowerCase()]=function(e){return!!t.data(e,o)},t[c]=t[c]||{},a=t[c][i],r=t[c][i]=function(t,i){return this._createWidget?(arguments.length&&this._createWidget(t,i),e):new r(t,i)},t.extend(r,a,{version:n.version,_proto:t.extend({},n),_childConstructors:[]}),h=new s,h.options=t.widget.extend({},h.options),t.each(n,function(i,n){return t.isFunction(n)?(l[i]=function(){var t=function(){return s.prototype[i].apply(this,arguments)},e=function(t){return s.prototype[i].apply(this,t)};return function(){var i,s=this._super,o=this._superApply;return this._super=t,this._superApply=e,i=n.apply(this,arguments),this._super=s,this._superApply=o,i}}(),e):(l[i]=n,e)}),r.prototype=t.widget.extend(h,{widgetEventPrefix:a?h.widgetEventPrefix||i:i},l,{constructor:r,namespace:c,widgetName:i,widgetFullName:o}),a?(t.each(a._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,r,i._proto)}),delete a._childConstructors):s._childConstructors.push(r),t.widget.bridge(i,r)},t.widget.extend=function(i){for(var n,o,a=s.call(arguments,1),r=0,h=a.length;h>r;r++)for(n in a[r])o=a[r][n],a[r].hasOwnProperty(n)&&o!==e&&(i[n]=t.isPlainObject(o)?t.isPlainObject(i[n])?t.widget.extend({},i[n],o):t.widget.extend({},o):o);return i},t.widget.bridge=function(i,n){var o=n.prototype.widgetFullName||i;t.fn[i]=function(a){var r="string"==typeof a,h=s.call(arguments,1),l=this;return a=!r&&h.length?t.widget.extend.apply(null,[a].concat(h)):a,r?this.each(function(){var s,n=t.data(this,o);return n?t.isFunction(n[a])&&"_"!==a.charAt(0)?(s=n[a].apply(n,h),s!==n&&s!==e?(l=s&&s.jquery?l.pushStack(s.get()):s,!1):e):t.error("no such method '"+a+"' for "+i+" widget instance"):t.error("cannot call methods on "+i+" prior to initialization; "+"attempted to call method '"+a+"'")}):this.each(function(){var e=t.data(this,o);e?e.option(a||{})._init():t.data(this,o,new n(a,this))}),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this.bindings=t(),this.hoverable=t(),this.focusable=t(),s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:t.noop,_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetName).removeData(this.widgetFullName).removeData(t.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:t.noop,widget:function(){return this.element},option:function(i,s){var n,o,a,r=i;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof i)if(r={},n=i.split("."),i=n.shift(),n.length){for(o=r[i]=t.widget.extend({},this.options[i]),a=0;n.length-1>a;a++)o[n[a]]=o[n[a]]||{},o=o[n[a]];if(i=n.pop(),1===arguments.length)return o[i]===e?null:o[i];o[i]=s}else{if(1===arguments.length)return this.options[i]===e?null:this.options[i];r[i]=s}return this._setOptions(r),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return this.options[t]=e,"disabled"===t&&(this.widget().toggleClass(this.widgetFullName+"-disabled ui-state-disabled",!!e).attr("aria-disabled",e),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")),this},enable:function(){return this._setOption("disabled",!1)},disable:function(){return this._setOption("disabled",!0)},_on:function(i,s,n){var o,a=this;"boolean"!=typeof i&&(n=s,s=i,i=!1),n?(s=o=t(s),this.bindings=this.bindings.add(s)):(n=s,s=this.element,o=this.widget()),t.each(n,function(n,r){function h(){return i||a.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof r?a[r]:r).apply(a,arguments):e}"string"!=typeof r&&(h.guid=r.guid=r.guid||h.guid||t.guid++);var l=n.match(/^(\w+)\s*(.*)$/),c=l[1]+a.eventNamespace,u=l[2];u?o.delegate(u,c,h):s.bind(c,h)})},_off:function(t,e){e=(e||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,t.unbind(e).undelegate(e)},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){t(e.currentTarget).addClass("ui-state-hover")},mouseleave:function(e){t(e.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){t(e.currentTarget).addClass("ui-state-focus")},focusout:function(e){t(e.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}})})(jQuery);(function(t){var e=!1;t(document).mouseup(function(){e=!1}),t.widget("ui.mouse",{version:"1.10.4",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.bind("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).bind("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):undefined}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(i){if(!e){this._mouseStarted&&this._mouseUp(i),this._mouseDownEvent=i;var s=this,n=1===i.which,a="string"==typeof this.options.cancel&&i.target.nodeName?t(i.target).closest(this.options.cancel).length:!1;return n&&!a&&this._mouseCapture(i)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){s.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(i)&&this._mouseDelayMet(i)&&(this._mouseStarted=this._mouseStart(i)!==!1,!this._mouseStarted)?(i.preventDefault(),!0):(!0===t.data(i.target,this.widgetName+".preventClickEvent")&&t.removeData(i.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return s._mouseMove(t)},this._mouseUpDelegate=function(t){return s._mouseUp(t)},t(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),i.preventDefault(),e=!0,!0)):!0}},_mouseMove:function(e){return t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button?this._mouseUp(e):this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){return t(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),!1},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}})})(jQuery);(function(t,e){function i(t,e,i){return[parseFloat(t[0])*(p.test(t[0])?e/100:1),parseFloat(t[1])*(p.test(t[1])?i/100:1)]}function s(e,i){return parseInt(t.css(e,i),10)||0}function n(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}t.ui=t.ui||{};var a,o=Math.max,r=Math.abs,l=Math.round,h=/left|center|right/,c=/top|center|bottom/,u=/[\+\-]\d+(\.[\d]+)?%?/,d=/^\w+/,p=/%$/,f=t.fn.position;t.position={scrollbarWidth:function(){if(a!==e)return a;var i,s,n=t("
"),o=n.children()[0];return t("body").append(n),i=o.offsetWidth,n.css("overflow","scroll"),s=o.offsetWidth,i===s&&(s=n[0].clientWidth),n.remove(),a=i-s},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widths?"left":i>0?"right":"center",vertical:0>a?"top":n>0?"bottom":"middle"};u>p&&p>r(i+s)&&(l.horizontal="center"),d>g&&g>r(n+a)&&(l.vertical="middle"),l.important=o(r(i),r(s))>o(r(n),r(a))?"horizontal":"vertical",e.using.call(this,t,l)}),c.offset(t.extend(M,{using:h}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,a=n.offset.left+n.scrollLeft,o=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-o-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-o-a,(0>i||r(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>r(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,a=n.offset.top+n.scrollTop,o=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-o-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-o-a,t.top+p+f+g>c&&(0>s||r(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,t.top+p+f+g>u&&(i>0||u>r(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}},function(){var e,i,s,n,a,o=document.getElementsByTagName("body")[0],r=document.createElement("div");e=document.createElement(o?"div":"body"),s={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&t.extend(s,{position:"absolute",left:"-1000px",top:"-1000px"});for(a in s)e.style[a]=s[a];e.appendChild(r),i=o||document.documentElement,i.insertBefore(e,i.firstChild),r.style.cssText="position: absolute; left: 10.7432222px;",n=t(r).offset().left,t.support.offsetFractions=n>10&&11>n,e.innerHTML="",i.removeChild(e)}()})(jQuery);(function(t){t.widget("ui.draggable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._mouseInit()},_destroy:function(){this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._mouseDestroy()},_mouseCapture:function(e){var i=this.options;return this.helper||i.disabled||t(e.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(e),this.handle?(t(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){t("
").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(t(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(e){var i=this.options;return this.helper=this._createHelper(e),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),t.ui.ddmanager&&(t.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,i.cursorAt&&this._adjustOffsetFromHelper(i.cursorAt),this._setContainment(),this._trigger("start",e)===!1?(this._clear(),!1):(this._cacheHelperProportions(),t.ui.ddmanager&&!i.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this._mouseDrag(e,!0),t.ui.ddmanager&&t.ui.ddmanager.dragStart(this,e),!0)},_mouseDrag:function(e,i){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),!i){var s=this._uiHash();if(this._trigger("drag",e,s)===!1)return this._mouseUp({}),!1;this.position=s.position}return this.options.axis&&"y"===this.options.axis||(this.helper[0].style.left=this.position.left+"px"),this.options.axis&&"x"===this.options.axis||(this.helper[0].style.top=this.position.top+"px"),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),!1},_mouseStop:function(e){var i=this,s=!1;return t.ui.ddmanager&&!this.options.dropBehaviour&&(s=t.ui.ddmanager.drop(this,e)),this.dropped&&(s=this.dropped,this.dropped=!1),"original"!==this.options.helper||t.contains(this.element[0].ownerDocument,this.element[0])?("invalid"===this.options.revert&&!s||"valid"===this.options.revert&&s||this.options.revert===!0||t.isFunction(this.options.revert)&&this.options.revert.call(this.element,s)?t(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){i._trigger("stop",e)!==!1&&i._clear()}):this._trigger("stop",e)!==!1&&this._clear(),!1):!1},_mouseUp:function(e){return t("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),t.ui.ddmanager&&t.ui.ddmanager.dragStop(this,e),t.ui.mouse.prototype._mouseUp.call(this,e)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(e){return this.options.handle?!!t(e.target).closest(this.element.find(this.options.handle)).length:!0},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e])):"clone"===i.helper?this.element.clone().removeAttr("id"):this.element;return s.parents("body").length||s.appendTo("parent"===i.appendTo?this.element[0].parentNode:i.appendTo),s[0]===this.element[0]||/(fixed|absolute)/.test(s.css("position"))||s.css("position","absolute"),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.element.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;return n.containment?"window"===n.containment?(this.containment=[t(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,t(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,t(window).scrollLeft()+t(window).width()-this.helperProportions.width-this.margins.left,t(window).scrollTop()+(t(window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):"document"===n.containment?(this.containment=[0,0,t(document).width()-this.helperProportions.width-this.margins.left,(t(document).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],undefined):n.containment.constructor===Array?(this.containment=n.containment,undefined):("parent"===n.containment&&(n.containment=this.helper[0].parentNode),i=t(n.containment),s=i[0],s&&(e="hidden"!==i.css("overflow"),this.containment=[(parseInt(i.css("borderLeftWidth"),10)||0)+(parseInt(i.css("paddingLeft"),10)||0),(parseInt(i.css("borderTopWidth"),10)||0)+(parseInt(i.css("paddingTop"),10)||0),(e?Math.max(s.scrollWidth,s.offsetWidth):s.offsetWidth)-(parseInt(i.css("borderRightWidth"),10)||0)-(parseInt(i.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(e?Math.max(s.scrollHeight,s.offsetHeight):s.offsetHeight)-(parseInt(i.css("borderBottomWidth"),10)||0)-(parseInt(i.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=i),undefined):(this.containment=null,undefined)},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent;return this.offset.scroll||(this.offset.scroll={top:n.scrollTop(),left:n.scrollLeft()}),{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top)*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)*s}},_generatePosition:function(e){var i,s,n,a,o=this.options,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,l=e.pageX,h=e.pageY;return this.offset.scroll||(this.offset.scroll={top:r.scrollTop(),left:r.scrollLeft()}),this.originalPosition&&(this.containment&&(this.relative_container?(s=this.relative_container.offset(),i=[this.containment[0]+s.left,this.containment[1]+s.top,this.containment[2]+s.left,this.containment[3]+s.top]):i=this.containment,e.pageX-this.offset.click.lefti[2]&&(l=i[2]+this.offset.click.left),e.pageY-this.offset.click.top>i[3]&&(h=i[3]+this.offset.click.top)),o.grid&&(n=o.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/o.grid[1])*o.grid[1]:this.originalPageY,h=i?n-this.offset.click.top>=i[1]||n-this.offset.click.top>i[3]?n:n-this.offset.click.top>=i[1]?n-o.grid[1]:n+o.grid[1]:n,a=o.grid[0]?this.originalPageX+Math.round((l-this.originalPageX)/o.grid[0])*o.grid[0]:this.originalPageX,l=i?a-this.offset.click.left>=i[0]||a-this.offset.click.left>i[2]?a:a-this.offset.click.left>=i[0]?a-o.grid[0]:a+o.grid[0]:a)),{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():this.offset.scroll.top),left:l-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():this.offset.scroll.left)}},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1},_trigger:function(e,i,s){return s=s||this._uiHash(),t.ui.plugin.call(this,e,[i,s]),"drag"===e&&(this.positionAbs=this._convertPositionTo("absolute")),t.Widget.prototype._trigger.call(this,e,i,s)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),t.ui.plugin.add("draggable","connectToSortable",{start:function(e,i){var s=t(this).data("ui-draggable"),n=s.options,a=t.extend({},i,{item:s.element});s.sortables=[],t(n.connectToSortable).each(function(){var i=t.data(this,"ui-sortable");i&&!i.options.disabled&&(s.sortables.push({instance:i,shouldRevert:i.options.revert}),i.refreshPositions(),i._trigger("activate",e,a))})},stop:function(e,i){var s=t(this).data("ui-draggable"),n=t.extend({},i,{item:s.element});t.each(s.sortables,function(){this.instance.isOver?(this.instance.isOver=0,s.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(e),this.instance.options.helper=this.instance.options._helper,"original"===s.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",e,n))})},drag:function(e,i){var s=t(this).data("ui-draggable"),n=this;t.each(s.sortables,function(){var a=!1,o=this;this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(a=!0,t.each(s.sortables,function(){return this.instance.positionAbs=s.positionAbs,this.instance.helperProportions=s.helperProportions,this.instance.offset.click=s.offset.click,this!==o&&this.instance._intersectsWith(this.instance.containerCache)&&t.contains(o.instance.element[0],this.instance.element[0])&&(a=!1),a})),a?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=t(n).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return i.helper[0]},e.target=this.instance.currentItem[0],this.instance._mouseCapture(e,!0),this.instance._mouseStart(e,!0,!0),this.instance.offset.click.top=s.offset.click.top,this.instance.offset.click.left=s.offset.click.left,this.instance.offset.parent.left-=s.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=s.offset.parent.top-this.instance.offset.parent.top,s._trigger("toSortable",e),s.dropped=this.instance.element,s.currentItem=s.element,this.instance.fromOutside=s),this.instance.currentItem&&this.instance._mouseDrag(e)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",e,this.instance._uiHash(this.instance)),this.instance._mouseStop(e,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),s._trigger("fromSortable",e),s.dropped=!1)})}}),t.ui.plugin.add("draggable","cursor",{start:function(){var e=t("body"),i=t(this).data("ui-draggable").options;e.css("cursor")&&(i._cursor=e.css("cursor")),e.css("cursor",i.cursor)},stop:function(){var e=t(this).data("ui-draggable").options;e._cursor&&t("body").css("cursor",e._cursor)}}),t.ui.plugin.add("draggable","opacity",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("opacity")&&(n._opacity=s.css("opacity")),s.css("opacity",n.opacity)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._opacity&&t(i.helper).css("opacity",s._opacity)}}),t.ui.plugin.add("draggable","scroll",{start:function(){var e=t(this).data("ui-draggable");e.scrollParent[0]!==document&&"HTML"!==e.scrollParent[0].tagName&&(e.overflowOffset=e.scrollParent.offset())},drag:function(e){var i=t(this).data("ui-draggable"),s=i.options,n=!1;i.scrollParent[0]!==document&&"HTML"!==i.scrollParent[0].tagName?(s.axis&&"x"===s.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-e.pageY=0;u--)r=p.snapElements[u].left,l=r+p.snapElements[u].width,h=p.snapElements[u].top,c=h+p.snapElements[u].height,r-f>_||m>l+f||h-f>b||v>c+f||!t.contains(p.snapElements[u].item.ownerDocument,p.snapElements[u].item)?(p.snapElements[u].snapping&&p.options.snap.release&&p.options.snap.release.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=!1):("inner"!==g.snapMode&&(s=f>=Math.abs(h-b),n=f>=Math.abs(c-v),a=f>=Math.abs(r-_),o=f>=Math.abs(l-m),s&&(i.position.top=p._convertPositionTo("relative",{top:h-p.helperProportions.height,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r-p.helperProportions.width}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l}).left-p.margins.left)),d=s||n||a||o,"outer"!==g.snapMode&&(s=f>=Math.abs(h-v),n=f>=Math.abs(c-b),a=f>=Math.abs(r-m),o=f>=Math.abs(l-_),s&&(i.position.top=p._convertPositionTo("relative",{top:h,left:0}).top-p.margins.top),n&&(i.position.top=p._convertPositionTo("relative",{top:c-p.helperProportions.height,left:0}).top-p.margins.top),a&&(i.position.left=p._convertPositionTo("relative",{top:0,left:r}).left-p.margins.left),o&&(i.position.left=p._convertPositionTo("relative",{top:0,left:l-p.helperProportions.width}).left-p.margins.left)),!p.snapElements[u].snapping&&(s||n||a||o||d)&&p.options.snap.snap&&p.options.snap.snap.call(p.element,e,t.extend(p._uiHash(),{snapItem:p.snapElements[u].item})),p.snapElements[u].snapping=s||n||a||o||d)}}),t.ui.plugin.add("draggable","stack",{start:function(){var e,i=this.data("ui-draggable").options,s=t.makeArray(t(i.stack)).sort(function(e,i){return(parseInt(t(e).css("zIndex"),10)||0)-(parseInt(t(i).css("zIndex"),10)||0)});s.length&&(e=parseInt(t(s[0]).css("zIndex"),10)||0,t(s).each(function(i){t(this).css("zIndex",e+i)}),this.css("zIndex",e+s.length))}}),t.ui.plugin.add("draggable","zIndex",{start:function(e,i){var s=t(i.helper),n=t(this).data("ui-draggable").options;s.css("zIndex")&&(n._zIndex=s.css("zIndex")),s.css("zIndex",n.zIndex)},stop:function(e,i){var s=t(this).data("ui-draggable").options;s._zIndex&&t(i.helper).css("zIndex",s._zIndex)}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}t.widget("ui.droppable",{version:"1.10.4",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var e,i=this.options,s=i.accept;this.isover=!1,this.isout=!0,this.accept=t.isFunction(s)?s:function(t){return t.is(s)},this.proportions=function(){return arguments.length?(e=arguments[0],undefined):e?e:e={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},t.ui.ddmanager.droppables[i.scope]=t.ui.ddmanager.droppables[i.scope]||[],t.ui.ddmanager.droppables[i.scope].push(this),i.addClasses&&this.element.addClass("ui-droppable")},_destroy:function(){for(var e=0,i=t.ui.ddmanager.droppables[this.options.scope];i.length>e;e++)i[e]===this&&i.splice(e,1);this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(e,i){"accept"===e&&(this.accept=t.isFunction(i)?i:function(t){return t.is(i)}),t.Widget.prototype._setOption.apply(this,arguments)},_activate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),i&&this._trigger("activate",e,this.ui(i))},_deactivate:function(e){var i=t.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),i&&this._trigger("deactivate",e,this.ui(i))},_over:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",e,this.ui(i)))},_out:function(e){var i=t.ui.ddmanager.current;i&&(i.currentItem||i.element)[0]!==this.element[0]&&this.accept.call(this.element[0],i.currentItem||i.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",e,this.ui(i)))},_drop:function(e,i){var s=i||t.ui.ddmanager.current,n=!1;return s&&(s.currentItem||s.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var e=t.data(this,"ui-droppable");return e.options.greedy&&!e.options.disabled&&e.options.scope===s.options.scope&&e.accept.call(e.element[0],s.currentItem||s.element)&&t.ui.intersect(s,t.extend(e,{offset:e.element.offset()}),e.options.tolerance)?(n=!0,!1):undefined}),n?!1:this.accept.call(this.element[0],s.currentItem||s.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",e,this.ui(s)),this.element):!1):!1},ui:function(t){return{draggable:t.currentItem||t.element,helper:t.helper,position:t.position,offset:t.positionAbs}}}),t.ui.intersect=function(t,i,s){if(!i.offset)return!1;var n,a,o=(t.positionAbs||t.position.absolute).left,r=(t.positionAbs||t.position.absolute).top,l=o+t.helperProportions.width,h=r+t.helperProportions.height,c=i.offset.left,u=i.offset.top,d=c+i.proportions().width,p=u+i.proportions().height;switch(s){case"fit":return o>=c&&d>=l&&r>=u&&p>=h;case"intersect":return o+t.helperProportions.width/2>c&&d>l-t.helperProportions.width/2&&r+t.helperProportions.height/2>u&&p>h-t.helperProportions.height/2;case"pointer":return n=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,a=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(a,u,i.proportions().height)&&e(n,c,i.proportions().width);case"touch":return(r>=u&&p>=r||h>=u&&p>=h||u>r&&h>p)&&(o>=c&&d>=o||l>=c&&d>=l||c>o&&l>d);default:return!1}},t.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(e,i){var s,n,a=t.ui.ddmanager.droppables[e.options.scope]||[],o=i?i.type:null,r=(e.currentItem||e.element).find(":data(ui-droppable)").addBack();t:for(s=0;a.length>s;s++)if(!(a[s].options.disabled||e&&!a[s].accept.call(a[s].element[0],e.currentItem||e.element))){for(n=0;r.length>n;n++)if(r[n]===a[s].element[0]){a[s].proportions().height=0;continue t}a[s].visible="none"!==a[s].element.css("display"),a[s].visible&&("mousedown"===o&&a[s]._activate.call(a[s],i),a[s].offset=a[s].element.offset(),a[s].proportions({width:a[s].element[0].offsetWidth,height:a[s].element[0].offsetHeight}))}},drop:function(e,i){var s=!1;return t.each((t.ui.ddmanager.droppables[e.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&t.ui.intersect(e,this,this.options.tolerance)&&(s=this._drop.call(this,i)||s),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],e.currentItem||e.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,i)))}),s},dragStart:function(e,i){e.element.parentsUntil("body").bind("scroll.droppable",function(){e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)})},drag:function(e,i){e.options.refreshPositions&&t.ui.ddmanager.prepareOffsets(e,i),t.each(t.ui.ddmanager.droppables[e.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var s,n,a,o=t.ui.intersect(e,this,this.options.tolerance),r=!o&&this.isover?"isout":o&&!this.isover?"isover":null;r&&(this.options.greedy&&(n=this.options.scope,a=this.element.parents(":data(ui-droppable)").filter(function(){return t.data(this,"ui-droppable").options.scope===n}),a.length&&(s=t.data(a[0],"ui-droppable"),s.greedyChild="isover"===r)),s&&"isover"===r&&(s.isover=!1,s.isout=!0,s._out.call(s,i)),this[r]=!0,this["isout"===r?"isover":"isout"]=!1,this["isover"===r?"_over":"_out"].call(this,i),s&&"isout"===r&&(s.isout=!1,s.isover=!0,s._over.call(s,i)))}})},dragStop:function(e,i){e.element.parentsUntil("body").unbind("scroll.droppable"),e.options.refreshPositions||t.ui.ddmanager.prepareOffsets(e,i)}}})(jQuery);(function(t){function e(t){return parseInt(t,10)||0}function i(t){return!isNaN(parseInt(t,10))}t.widget("ui.resizable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_create:function(){var e,i,s,n,a,o=this,r=this.options;if(this.element.addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!r.aspectRatio,aspectRatio:r.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:r.helper||r.ghost||r.animate?r.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(t("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.data("ui-resizable")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=r.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),e=this.handles.split(","),this.handles={},i=0;e.length>i;i++)s=t.trim(e[i]),a="ui-resizable-"+s,n=t("
"),n.css({zIndex:r.zIndex}),"se"===s&&n.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[s]=".ui-resizable-"+s,this.element.append(n);this._renderAxis=function(e){var i,s,n,a;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String&&(this.handles[i]=t(this.handles[i],this.element).show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(s=t(this.handles[i],this.element),a=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,a),this._proportionallyResize()),t(this.handles[i]).length},this._renderAxis(this.element),this._handles=t(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){o.resizing||(this.className&&(n=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),o.axis=n&&n[1]?n[1]:"se")}),r.autoHide&&(this._handles.hide(),t(this.element).addClass("ui-resizable-autohide").mouseenter(function(){r.disabled||(t(this).removeClass("ui-resizable-autohide"),o._handles.show())}).mouseleave(function(){r.disabled||o.resizing||(t(this).addClass("ui-resizable-autohide"),o._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(i){var s,n,a,o=this.options,r=this.element.position(),h=this.element;return this.resizing=!0,/absolute/.test(h.css("position"))?h.css({position:"absolute",top:h.css("top"),left:h.css("left")}):h.is(".ui-draggable")&&h.css({position:"absolute",top:r.top,left:r.left}),this._renderProxy(),s=e(this.helper.css("left")),n=e(this.helper.css("top")),o.containment&&(s+=t(o.containment).scrollLeft()||0,n+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:s,top:n},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:h.width(),height:h.height()},this.originalSize=this._helper?{width:h.outerWidth(),height:h.outerHeight()}:{width:h.width(),height:h.height()},this.originalPosition={left:s,top:n},this.sizeDiff={width:h.outerWidth()-h.width(),height:h.outerHeight()-h.height()},this.originalMousePosition={left:i.pageX,top:i.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,a=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===a?this.axis+"-resize":a),h.addClass("ui-resizable-resizing"),this._propagate("start",i),!0},_mouseDrag:function(e){var i,s=this.helper,n={},a=this.originalMousePosition,o=this.axis,r=this.position.top,h=this.position.left,l=this.size.width,c=this.size.height,u=e.pageX-a.left||0,d=e.pageY-a.top||0,p=this._change[o];return p?(i=p.apply(this,[e,u,d]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),this.position.top!==r&&(n.top=this.position.top+"px"),this.position.left!==h&&(n.left=this.position.left+"px"),this.size.width!==l&&(n.width=this.size.width+"px"),this.size.height!==c&&(n.height=this.size.height+"px"),s.css(n),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(n)||this._trigger("resize",e,this.ui()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,a,o,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&t.ui.hasScroll(i[0],"left")?0:c.sizeDiff.height,a=s?0:c.sizeDiff.width,o={width:c.helper.width()-a,height:c.helper.height()-n},r=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null,h=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(o,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(t){var e,s,n,a,o,r=this.options;o={minWidth:i(r.minWidth)?r.minWidth:0,maxWidth:i(r.maxWidth)?r.maxWidth:1/0,minHeight:i(r.minHeight)?r.minHeight:0,maxHeight:i(r.maxHeight)?r.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,n=o.minWidth/this.aspectRatio,s=o.maxHeight*this.aspectRatio,a=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),n>o.minHeight&&(o.minHeight=n),o.maxWidth>s&&(o.maxWidth=s),o.maxHeight>a&&(o.maxHeight=a)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),i(t.left)&&(this.position.left=t.left),i(t.top)&&(this.position.top=t.top),i(t.height)&&(this.size.height=t.height),i(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,s=this.size,n=this.axis;return i(t.height)?t.width=t.height*this.aspectRatio:i(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===n&&(t.left=e.left+(s.width-t.width),t.top=null),"nw"===n&&(t.top=e.top+(s.height-t.height),t.left=e.left+(s.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,s=this.axis,n=i(t.width)&&e.maxWidth&&e.maxWidtht.width,r=i(t.height)&&e.minHeight&&e.minHeight>t.height,h=this.originalPosition.left+this.originalSize.width,l=this.position.top+this.size.height,c=/sw|nw|w/.test(s),u=/nw|ne|n/.test(s);return o&&(t.width=e.minWidth),r&&(t.height=e.minHeight),n&&(t.width=e.maxWidth),a&&(t.height=e.maxHeight),o&&c&&(t.left=h-e.minWidth),n&&c&&(t.left=h-e.maxWidth),r&&u&&(t.top=l-e.minHeight),a&&u&&(t.top=l-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var t,e,i,s,n,a=this.helper||this.element;for(t=0;this._proportionallyResizeElements.length>t;t++){if(n=this._proportionallyResizeElements[t],!this.borderDif)for(this.borderDif=[],i=[n.css("borderTopWidth"),n.css("borderRightWidth"),n.css("borderBottomWidth"),n.css("borderLeftWidth")],s=[n.css("paddingTop"),n.css("paddingRight"),n.css("paddingBottom"),n.css("paddingLeft")],e=0;i.length>e;e++)this.borderDif[e]=(parseInt(i[e],10)||0)+(parseInt(s[e],10)||0);n.css({height:a.height()-this.borderDif[0]-this.borderDif[2]||0,width:a.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).data("ui-resizable"),s=i.options,n=i._proportionallyResizeElements,a=n.length&&/textarea/i.test(n[0].nodeName),o=a&&t.ui.hasScroll(n[0],"left")?0:i.sizeDiff.height,r=a?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-o},l=parseInt(i.element.css("left"),10)+(i.position.left-i.originalPosition.left)||null,c=parseInt(i.element.css("top"),10)+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseInt(i.element.css("width"),10),height:parseInt(i.element.css("height"),10),top:parseInt(i.element.css("top"),10),left:parseInt(i.element.css("left"),10)};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var i,s,n,a,o,r,h,l=t(this).data("ui-resizable"),c=l.options,u=l.element,d=c.containment,p=d instanceof t?d.get(0):/parent/.test(d)?u.parent().get(0):d;p&&(l.containerElement=t(p),/document/.test(d)||d===document?(l.containerOffset={left:0,top:0},l.containerPosition={left:0,top:0},l.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(i=t(p),s=[],t(["Top","Right","Left","Bottom"]).each(function(t,n){s[t]=e(i.css("padding"+n))}),l.containerOffset=i.offset(),l.containerPosition=i.position(),l.containerSize={height:i.innerHeight()-s[3],width:i.innerWidth()-s[1]},n=l.containerOffset,a=l.containerSize.height,o=l.containerSize.width,r=t.ui.hasScroll(p,"left")?p.scrollWidth:o,h=t.ui.hasScroll(p)?p.scrollHeight:a,l.parentData={element:p,left:n.left,top:n.top,width:r,height:h}))},resize:function(e){var i,s,n,a,o=t(this).data("ui-resizable"),r=o.options,h=o.containerOffset,l=o.position,c=o._aspectRatio||e.shiftKey,u={top:0,left:0},d=o.containerElement;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(o._helper?h.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-h.left:o.position.left-u.left),c&&(o.size.height=o.size.width/o.aspectRatio),o.position.left=r.helper?h.left:0),l.top<(o._helper?h.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-h.top:o.position.top),c&&(o.size.width=o.size.height*o.aspectRatio),o.position.top=o._helper?h.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-u.left:o.offset.left-u.left)+o.sizeDiff.width),s=Math.abs((o._helper?o.offset.top-u.top:o.offset.top-h.top)+o.sizeDiff.height),n=o.containerElement.get(0)===o.element.parent().get(0),a=/relative|absolute/.test(o.containerElement.css("position")),n&&a&&(i-=Math.abs(o.parentData.left)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,c&&(o.size.height=o.size.width/o.aspectRatio)),s+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-s,c&&(o.size.width=o.size.height*o.aspectRatio))},stop:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.containerOffset,n=e.containerPosition,a=e.containerElement,o=t(e.helper),r=o.offset(),h=o.outerWidth()-e.sizeDiff.width,l=o.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(a.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=function(e){t(e).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseInt(e.width(),10),height:parseInt(e.height(),10),left:parseInt(e.css("left"),10),top:parseInt(e.css("top"),10)})})};"object"!=typeof i.alsoResize||i.alsoResize.parentNode?s(i.alsoResize):i.alsoResize.length?(i.alsoResize=i.alsoResize[0],s(i.alsoResize)):t.each(i.alsoResize,function(t){s(t)})},resize:function(e,i){var s=t(this).data("ui-resizable"),n=s.options,a=s.originalSize,o=s.originalPosition,r={height:s.size.height-a.height||0,width:s.size.width-a.width||0,top:s.position.top-o.top||0,left:s.position.left-o.left||0},h=function(e,s){t(e).each(function(){var e=t(this),n=t(this).data("ui-resizable-alsoresize"),a={},o=s&&s.length?s:e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(n[e]||0)+(r[e]||0);i&&i>=0&&(a[e]=i||null)}),e.css(a)})};"object"!=typeof n.alsoResize||n.alsoResize.nodeType?h(n.alsoResize):t.each(n.alsoResize,function(t,e){h(t,e)})},stop:function(){t(this).removeData("resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:s.height,width:s.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof i.ghost?i.ghost:""),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).data("ui-resizable");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).data("ui-resizable");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e=t(this).data("ui-resizable"),i=e.options,s=e.size,n=e.originalSize,a=e.originalPosition,o=e.axis,r="number"==typeof i.grid?[i.grid,i.grid]:i.grid,h=r[0]||1,l=r[1]||1,c=Math.round((s.width-n.width)/h)*h,u=Math.round((s.height-n.height)/l)*l,d=n.width+c,p=n.height+u,f=i.maxWidth&&d>i.maxWidth,g=i.maxHeight&&p>i.maxHeight,m=i.minWidth&&i.minWidth>d,v=i.minHeight&&i.minHeight>p;i.grid=r,m&&(d+=h),v&&(p+=l),f&&(d-=h),g&&(p-=l),/^(se|s|e)$/.test(o)?(e.size.width=d,e.size.height=p):/^(ne)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.top=a.top-u):/^(sw)$/.test(o)?(e.size.width=d,e.size.height=p,e.position.left=a.left-c):(p-l>0?(e.size.height=p,e.position.top=a.top-u):(e.size.height=l,e.position.top=a.top+n.height-l),d-h>0?(e.size.width=d,e.position.left=a.left-c):(e.size.width=h,e.position.left=a.left+n.width-h))}})})(jQuery);(function(t){function e(t,e,i){return t>e&&e+i>t}function i(t){return/left|right/.test(t.css("float"))||/inline|table-cell/.test(t.css("display"))}t.widget("ui.sortable",t.ui.mouse,{version:"1.10.4",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_create:function(){var t=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===t.axis||i(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this.ready=!0},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled"),this._mouseDestroy();for(var t=this.items.length-1;t>=0;t--)this.items[t].item.removeData(this.widgetName+"-item");return this},_setOption:function(e,i){"disabled"===e?(this.options[e]=i,this.widget().toggleClass("ui-sortable-disabled",!!i)):t.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(e,i){var s=null,n=!1,o=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(e),t(e.target).parents().each(function(){return t.data(this,o.widgetName+"-item")===o?(s=t(this),!1):undefined}),t.data(e.target,o.widgetName+"-item")===o&&(s=t(e.target)),s?!this.options.handle||i||(t(this.options.handle,s).find("*").addBack().each(function(){this===e.target&&(n=!0)}),n)?(this.currentItem=s,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(e,i,s){var n,o,a=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(e),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},t.extend(this.offset,{click:{left:e.pageX-this.offset.left,top:e.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(e),this.originalPageX=e.pageX,this.originalPageY=e.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),a.containment&&this._setContainment(),a.cursor&&"auto"!==a.cursor&&(o=this.document.find("body"),this.storedCursor=o.css("cursor"),o.css("cursor",a.cursor),this.storedStylesheet=t("").appendTo(o)),a.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",a.opacity)),a.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",a.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",e,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!s)for(n=this.containers.length-1;n>=0;n--)this.containers[n]._trigger("activate",e,this._uiHash(this));return t.ui.ddmanager&&(t.ui.ddmanager.current=this),t.ui.ddmanager&&!a.dropBehaviour&&t.ui.ddmanager.prepareOffsets(this,e),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(e),!0},_mouseDrag:function(e){var i,s,n,o,a=this.options,r=!1;for(this.position=this._generatePosition(e),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-e.pageY=0;i--)if(s=this.items[i],n=s.item[0],o=this._intersectsWithPointer(s),o&&s.instance===this.currentContainer&&n!==this.currentItem[0]&&this.placeholder[1===o?"next":"prev"]()[0]!==n&&!t.contains(this.placeholder[0],n)&&("semi-dynamic"===this.options.type?!t.contains(this.element[0],n):!0)){if(this.direction=1===o?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(s))break;this._rearrange(e,s),this._trigger("change",e,this._uiHash());break}return this._contactContainers(e),t.ui.ddmanager&&t.ui.ddmanager.drag(this,e),this._trigger("sort",e,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(e,i){if(e){if(t.ui.ddmanager&&!this.options.dropBehaviour&&t.ui.ddmanager.drop(this,e),this.options.revert){var s=this,n=this.placeholder.offset(),o=this.options.axis,a={};o&&"x"!==o||(a.left=n.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),o&&"y"!==o||(a.top=n.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,t(this.helper).animate(a,parseInt(this.options.revert,10)||500,function(){s._clear(e)})}else this._clear(e,i);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var e=this.containers.length-1;e>=0;e--)this.containers[e]._trigger("deactivate",null,this._uiHash(this)),this.containers[e].containerCache.over&&(this.containers[e]._trigger("out",null,this._uiHash(this)),this.containers[e].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),t.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?t(this.domPosition.prev).after(this.currentItem):t(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},t(i).each(function(){var i=(t(e.item||this).attr(e.attribute||"id")||"").match(e.expression||/(.+)[\-=_](.+)/);i&&s.push((e.key||i[1]+"[]")+"="+(e.key&&e.expression?i[1]:i[2]))}),!s.length&&e.key&&s.push(e.key+"="),s.join("&")},toArray:function(e){var i=this._getItemsAsjQuery(e&&e.connected),s=[];return e=e||{},i.each(function(){s.push(t(e.item||this).attr(e.attribute||"id")||"")}),s},_intersectsWith:function(t){var e=this.positionAbs.left,i=e+this.helperProportions.width,s=this.positionAbs.top,n=s+this.helperProportions.height,o=t.left,a=o+t.width,r=t.top,h=r+t.height,l=this.offset.click.top,c=this.offset.click.left,u="x"===this.options.axis||s+l>r&&h>s+l,d="y"===this.options.axis||e+c>o&&a>e+c,p=u&&d;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>t[this.floating?"width":"height"]?p:e+this.helperProportions.width/2>o&&a>i-this.helperProportions.width/2&&s+this.helperProportions.height/2>r&&h>n-this.helperProportions.height/2},_intersectsWithPointer:function(t){var i="x"===this.options.axis||e(this.positionAbs.top+this.offset.click.top,t.top,t.height),s="y"===this.options.axis||e(this.positionAbs.left+this.offset.click.left,t.left,t.width),n=i&&s,o=this._getDragVerticalDirection(),a=this._getDragHorizontalDirection();return n?this.floating?a&&"right"===a||"down"===o?2:1:o&&("down"===o?2:1):!1},_intersectsWithSides:function(t){var i=e(this.positionAbs.top+this.offset.click.top,t.top+t.height/2,t.height),s=e(this.positionAbs.left+this.offset.click.left,t.left+t.width/2,t.width),n=this._getDragVerticalDirection(),o=this._getDragHorizontalDirection();return this.floating&&o?"right"===o&&s||"left"===o&&!s:n&&("down"===n&&i||"up"===n&&!i)},_getDragVerticalDirection:function(){var t=this.positionAbs.top-this.lastPositionAbs.top;return 0!==t&&(t>0?"down":"up")},_getDragHorizontalDirection:function(){var t=this.positionAbs.left-this.lastPositionAbs.left;return 0!==t&&(t>0?"right":"left")},refresh:function(t){return this._refreshItems(t),this.refreshPositions(),this},_connectWith:function(){var t=this.options;return t.connectWith.constructor===String?[t.connectWith]:t.connectWith},_getItemsAsjQuery:function(e){function i(){r.push(this)}var s,n,o,a,r=[],h=[],l=this._connectWith();if(l&&e)for(s=l.length-1;s>=0;s--)for(o=t(l[s]),n=o.length-1;n>=0;n--)a=t.data(o[n],this.widgetFullName),a&&a!==this&&!a.options.disabled&&h.push([t.isFunction(a.options.items)?a.options.items.call(a.element):t(a.options.items,a.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),a]);for(h.push([t.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):t(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),s=h.length-1;s>=0;s--)h[s][0].each(i);return t(r)},_removeCurrentsFromItems:function(){var e=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=t.grep(this.items,function(t){for(var i=0;e.length>i;i++)if(e[i]===t.item[0])return!1;return!0})},_refreshItems:function(e){this.items=[],this.containers=[this];var i,s,n,o,a,r,h,l,c=this.items,u=[[t.isFunction(this.options.items)?this.options.items.call(this.element[0],e,{item:this.currentItem}):t(this.options.items,this.element),this]],d=this._connectWith();if(d&&this.ready)for(i=d.length-1;i>=0;i--)for(n=t(d[i]),s=n.length-1;s>=0;s--)o=t.data(n[s],this.widgetFullName),o&&o!==this&&!o.options.disabled&&(u.push([t.isFunction(o.options.items)?o.options.items.call(o.element[0],e,{item:this.currentItem}):t(o.options.items,o.element),o]),this.containers.push(o));for(i=u.length-1;i>=0;i--)for(a=u[i][1],r=u[i][0],s=0,l=r.length;l>s;s++)h=t(r[s]),h.data(this.widgetName+"-item",a),c.push({item:h,instance:a,width:0,height:0,left:0,top:0})},refreshPositions:function(e){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var i,s,n,o;for(i=this.items.length-1;i>=0;i--)s=this.items[i],s.instance!==this.currentContainer&&this.currentContainer&&s.item[0]!==this.currentItem[0]||(n=this.options.toleranceElement?t(this.options.toleranceElement,s.item):s.item,e||(s.width=n.outerWidth(),s.height=n.outerHeight()),o=n.offset(),s.left=o.left,s.top=o.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(i=this.containers.length-1;i>=0;i--)o=this.containers[i].element.offset(),this.containers[i].containerCache.left=o.left,this.containers[i].containerCache.top=o.top,this.containers[i].containerCache.width=this.containers[i].element.outerWidth(),this.containers[i].containerCache.height=this.containers[i].element.outerHeight();return this},_createPlaceholder:function(e){e=e||this;var i,s=e.options;s.placeholder&&s.placeholder.constructor!==String||(i=s.placeholder,s.placeholder={element:function(){var s=e.currentItem[0].nodeName.toLowerCase(),n=t("<"+s+">",e.document[0]).addClass(i||e.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===s?e.currentItem.children().each(function(){t(" ",e.document[0]).attr("colspan",t(this).attr("colspan")||1).appendTo(n)}):"img"===s&&n.attr("src",e.currentItem.attr("src")),i||n.css("visibility","hidden"),n},update:function(t,n){(!i||s.forcePlaceholderSize)&&(n.height()||n.height(e.currentItem.innerHeight()-parseInt(e.currentItem.css("paddingTop")||0,10)-parseInt(e.currentItem.css("paddingBottom")||0,10)),n.width()||n.width(e.currentItem.innerWidth()-parseInt(e.currentItem.css("paddingLeft")||0,10)-parseInt(e.currentItem.css("paddingRight")||0,10)))}}),e.placeholder=t(s.placeholder.element.call(e.element,e.currentItem)),e.currentItem.after(e.placeholder),s.placeholder.update(e,e.placeholder)},_contactContainers:function(s){var n,o,a,r,h,l,c,u,d,p,f=null,g=null;for(n=this.containers.length-1;n>=0;n--)if(!t.contains(this.currentItem[0],this.containers[n].element[0]))if(this._intersectsWith(this.containers[n].containerCache)){if(f&&t.contains(this.containers[n].element[0],f.element[0]))continue;f=this.containers[n],g=n}else this.containers[n].containerCache.over&&(this.containers[n]._trigger("out",s,this._uiHash(this)),this.containers[n].containerCache.over=0);if(f)if(1===this.containers.length)this.containers[g].containerCache.over||(this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1);else{for(a=1e4,r=null,p=f.floating||i(this.currentItem),h=p?"left":"top",l=p?"width":"height",c=this.positionAbs[h]+this.offset.click[h],o=this.items.length-1;o>=0;o--)t.contains(this.containers[g].element[0],this.items[o].item[0])&&this.items[o].item[0]!==this.currentItem[0]&&(!p||e(this.positionAbs.top+this.offset.click.top,this.items[o].top,this.items[o].height))&&(u=this.items[o].item.offset()[h],d=!1,Math.abs(u-c)>Math.abs(u+this.items[o][l]-c)&&(d=!0,u+=this.items[o][l]),a>Math.abs(u-c)&&(a=Math.abs(u-c),r=this.items[o],this.direction=d?"up":"down"));if(!r&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[g])return;r?this._rearrange(s,r,null,!0):this._rearrange(s,null,this.containers[g].element,!0),this._trigger("change",s,this._uiHash()),this.containers[g]._trigger("change",s,this._uiHash(this)),this.currentContainer=this.containers[g],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[g]._trigger("over",s,this._uiHash(this)),this.containers[g].containerCache.over=1}},_createHelper:function(e){var i=this.options,s=t.isFunction(i.helper)?t(i.helper.apply(this.element[0],[e,this.currentItem])):"clone"===i.helper?this.currentItem.clone():this.currentItem;return s.parents("body").length||t("parent"!==i.appendTo?i.appendTo:this.currentItem[0].parentNode)[0].appendChild(s[0]),s[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!s[0].style.width||i.forceHelperSize)&&s.width(this.currentItem.width()),(!s[0].style.height||i.forceHelperSize)&&s.height(this.currentItem.height()),s},_adjustOffsetFromHelper:function(e){"string"==typeof e&&(e=e.split(" ")),t.isArray(e)&&(e={left:+e[0],top:+e[1]||0}),"left"in e&&(this.offset.click.left=e.left+this.margins.left),"right"in e&&(this.offset.click.left=this.helperProportions.width-e.right+this.margins.left),"top"in e&&(this.offset.click.top=e.top+this.margins.top),"bottom"in e&&(this.offset.click.top=this.helperProportions.height-e.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var e=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])&&(e.left+=this.scrollParent.scrollLeft(),e.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&t.ui.ie)&&(e={top:0,left:0}),{top:e.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:e.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var t=this.currentItem.position();return{top:t.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:t.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var e,i,s,n=this.options;"parent"===n.containment&&(n.containment=this.helper[0].parentNode),("document"===n.containment||"window"===n.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,t("document"===n.containment?document:window).width()-this.helperProportions.width-this.margins.left,(t("document"===n.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(n.containment)||(e=t(n.containment)[0],i=t(n.containment).offset(),s="hidden"!==t(e).css("overflow"),this.containment=[i.left+(parseInt(t(e).css("borderLeftWidth"),10)||0)+(parseInt(t(e).css("paddingLeft"),10)||0)-this.margins.left,i.top+(parseInt(t(e).css("borderTopWidth"),10)||0)+(parseInt(t(e).css("paddingTop"),10)||0)-this.margins.top,i.left+(s?Math.max(e.scrollWidth,e.offsetWidth):e.offsetWidth)-(parseInt(t(e).css("borderLeftWidth"),10)||0)-(parseInt(t(e).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,i.top+(s?Math.max(e.scrollHeight,e.offsetHeight):e.offsetHeight)-(parseInt(t(e).css("borderTopWidth"),10)||0)-(parseInt(t(e).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(e,i){i||(i=this.position);var s="absolute"===e?1:-1,n="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,o=/(html|body)/i.test(n[0].tagName);return{top:i.top+this.offset.relative.top*s+this.offset.parent.top*s-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():o?0:n.scrollTop())*s,left:i.left+this.offset.relative.left*s+this.offset.parent.left*s-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():o?0:n.scrollLeft())*s}},_generatePosition:function(e){var i,s,n=this.options,o=e.pageX,a=e.pageY,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&t.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,h=/(html|body)/i.test(r[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(e.pageX-this.offset.click.leftthis.containment[2]&&(o=this.containment[2]+this.offset.click.left),e.pageY-this.offset.click.top>this.containment[3]&&(a=this.containment[3]+this.offset.click.top)),n.grid&&(i=this.originalPageY+Math.round((a-this.originalPageY)/n.grid[1])*n.grid[1],a=this.containment?i-this.offset.click.top>=this.containment[1]&&i-this.offset.click.top<=this.containment[3]?i:i-this.offset.click.top>=this.containment[1]?i-n.grid[1]:i+n.grid[1]:i,s=this.originalPageX+Math.round((o-this.originalPageX)/n.grid[0])*n.grid[0],o=this.containment?s-this.offset.click.left>=this.containment[0]&&s-this.offset.click.left<=this.containment[2]?s:s-this.offset.click.left>=this.containment[0]?s-n.grid[0]:s+n.grid[0]:s)),{top:a-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():h?0:r.scrollTop()),left:o-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():h?0:r.scrollLeft())}},_rearrange:function(t,e,i,s){i?i[0].appendChild(this.placeholder[0]):e.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?e.item[0]:e.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var n=this.counter;this._delay(function(){n===this.counter&&this.refreshPositions(!s)})},_clear:function(t,e){function i(t,e,i){return function(s){i._trigger(t,s,e._uiHash(e))}}this.reverting=!1;var s,n=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(s in this._storedCSS)("auto"===this._storedCSS[s]||"static"===this._storedCSS[s])&&(this._storedCSS[s]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!e&&n.push(function(t){this._trigger("receive",t,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||e||n.push(function(t){this._trigger("update",t,this._uiHash())}),this!==this.currentContainer&&(e||(n.push(function(t){this._trigger("remove",t,this._uiHash())}),n.push(function(t){return function(e){t._trigger("receive",e,this._uiHash(this))}}.call(this,this.currentContainer)),n.push(function(t){return function(e){t._trigger("update",e,this._uiHash(this))}}.call(this,this.currentContainer)))),s=this.containers.length-1;s>=0;s--)e||n.push(i("deactivate",this,this.containers[s])),this.containers[s].containerCache.over&&(n.push(i("out",this,this.containers[s])),this.containers[s].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!e){for(this._trigger("beforeStop",t,this._uiHash()),s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!1}if(e||this._trigger("beforeStop",t,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!e){for(s=0;n.length>s;s++)n[s].call(this,t);this._trigger("stop",t,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){t.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(e){var i=e||this;return{helper:i.helper,placeholder:i.placeholder||t([]),position:i.position,originalPosition:i.originalPosition,offset:i.positionAbs,item:i.currentItem,sender:e?e.element:null}}})})(jQuery);(function(e){e.widget("ui.autocomplete",{version:"1.10.4",defaultElement:"",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,i,s,n=this.element[0].nodeName.toLowerCase(),a="textarea"===n,o="input"===n;this.isMultiLine=a?!0:o?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[a||o?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return t=!0,s=!0,i=!0,undefined;t=!1,s=!1,i=!1;var a=e.ui.keyCode;switch(n.keyCode){case a.PAGE_UP:t=!0,this._move("previousPage",n);break;case a.PAGE_DOWN:t=!0,this._move("nextPage",n);break;case a.UP:t=!0,this._keyEvent("previous",n);break;case a.DOWN:t=!0,this._keyEvent("next",n);break;case a.ENTER:case a.NUMPAD_ENTER:this.menu.active&&(t=!0,n.preventDefault(),this.menu.select(n));break;case a.TAB:this.menu.active&&this.menu.select(n);break;case a.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),undefined;if(!i){var n=e.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(e){return s?(s=!1,e.preventDefault(),undefined):(this._searchTimeout(e),undefined)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,undefined):(clearTimeout(this.searching),this.close(e),this._change(e),undefined)}}),this._initSource(),this.menu=e("
    ").addClass("ui-autocomplete ui-front").appendTo(this._appendTo()).menu({role:null}).hide().data("ui-menu"),this._on(this.menu.element,{mousedown:function(t){t.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur});var i=this.menu.element[0];e(t.target).closest(".ui-menu-item").length||this._delay(function(){var t=this;this.document.one("mousedown",function(s){s.target===t.element[0]||s.target===i||e.contains(i,s.target)||t.close()})})},menufocus:function(t,i){if(this.isNewMenu&&(this.isNewMenu=!1,t.originalEvent&&/^mouse/.test(t.originalEvent.type)))return this.menu.blur(),this.document.one("mousemove",function(){e(t.target).trigger(t.originalEvent)}),undefined;var s=i.item.data("ui-autocomplete-item");!1!==this._trigger("focus",t,{item:s})?t.originalEvent&&/^key/.test(t.originalEvent.type)&&this._value(s.value):this.liveRegion.text(s.value)},menuselect:function(e,t){var i=t.item.data("ui-autocomplete-item"),s=this.previous;this.element[0]!==this.document[0].activeElement&&(this.element.focus(),this.previous=s,this._delay(function(){this.previous=s,this.selectedItem=i})),!1!==this._trigger("select",e,{item:i})&&this._value(i.value),this.term=this._value(),this.close(e),this.selectedItem=i}}),this.liveRegion=e("",{role:"status","aria-live":"polite"}).addClass("ui-helper-hidden-accessible").insertBefore(this.element),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(e,t){this._super(e,t),"source"===e&&this._initSource(),"appendTo"===e&&this.menu.element.appendTo(this._appendTo()),"disabled"===e&&t&&this.xhr&&this.xhr.abort()},_appendTo:function(){var t=this.options.appendTo;return t&&(t=t.jquery||t.nodeType?e(t):this.document.find(t).eq(0)),t||(t=this.element.closest(".ui-front")),t.length||(t=this.document[0].body),t},_initSource:function(){var t,i,s=this;e.isArray(this.options.source)?(t=this.options.source,this.source=function(i,s){s(e.ui.autocomplete.filter(t,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(t,n){s.xhr&&s.xhr.abort(),s.xhr=e.ajax({url:i,data:t,dataType:"json",success:function(e){n(e)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(e){clearTimeout(this.searching),this.searching=this._delay(function(){this.term!==this._value()&&(this.selectedItem=null,this.search(null,e))},this.options.delay)},search:function(e,t){return e=null!=e?e:this._value(),this.term=this._value(),e.length").append(e("").text(i.label)).appendTo(t)},_move:function(e,t){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(e)||this.menu.isLastItem()&&/^next/.test(e)?(this._value(this.term),this.menu.blur(),undefined):(this.menu[e](t),undefined):(this.search(null,t),undefined)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(e,t){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(e,t),t.preventDefault())}}),e.extend(e.ui.autocomplete,{escapeRegex:function(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(t,i){var s=RegExp(e.ui.autocomplete.escapeRegex(i),"i");return e.grep(t,function(e){return s.test(e.label||e.value||e)})}}),e.widget("ui.autocomplete",e.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(e){return e+(e>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var t;this._superApply(arguments),this.options.disabled||this.cancelSearch||(t=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.text(t))}})})(jQuery);(function(e,t){function i(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.dpDiv=a(e("
    "))}function a(t){var i="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(i,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(i,"mouseover",function(){e.datepicker._isDisabledDatepicker(n.inline?t.parent()[0]:n.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function s(t,i){e.extend(t,i);for(var a in i)null==i[a]&&(t[a]=i[a]);return t}e.extend(e.ui,{datepicker:{version:"1.10.4"}});var n,r="datepicker";e.extend(i.prototype,{markerClassName:"hasDatepicker",maxRows:4,_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(e){return s(this._defaults,e||{}),this},_attachDatepicker:function(t,i){var a,s,n;a=t.nodeName.toLowerCase(),s="div"===a||"span"===a,t.id||(this.uuid+=1,t.id="dp"+this.uuid),n=this._newInst(e(t),s),n.settings=e.extend({},i||{}),"input"===a?this._connectDatepicker(t,n):s&&this._inlineDatepicker(t,n)},_newInst:function(t,i){var s=t[0].id.replace(/([^A-Za-z0-9_\-])/g,"\\\\$1");return{id:s,input:t,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:i,dpDiv:i?a(e("
    ")):this.dpDiv}},_connectDatepicker:function(t,i){var a=e(t);i.append=e([]),i.trigger=e([]),a.hasClass(this.markerClassName)||(this._attachments(a,i),a.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp),this._autoSize(i),e.data(t,r,i),i.settings.disabled&&this._disableDatepicker(t))},_attachments:function(t,i){var a,s,n,r=this._get(i,"appendText"),o=this._get(i,"isRTL");i.append&&i.append.remove(),r&&(i.append=e(""+r+""),t[o?"before":"after"](i.append)),t.unbind("focus",this._showDatepicker),i.trigger&&i.trigger.remove(),a=this._get(i,"showOn"),("focus"===a||"both"===a)&&t.focus(this._showDatepicker),("button"===a||"both"===a)&&(s=this._get(i,"buttonText"),n=this._get(i,"buttonImage"),i.trigger=e(this._get(i,"buttonImageOnly")?e("").addClass(this._triggerClass).attr({src:n,alt:s,title:s}):e("").addClass(this._triggerClass).html(n?e("").attr({src:n,alt:s,title:s}):s)),t[o?"before":"after"](i.trigger),i.trigger.click(function(){return e.datepicker._datepickerShowing&&e.datepicker._lastInput===t[0]?e.datepicker._hideDatepicker():e.datepicker._datepickerShowing&&e.datepicker._lastInput!==t[0]?(e.datepicker._hideDatepicker(),e.datepicker._showDatepicker(t[0])):e.datepicker._showDatepicker(t[0]),!1}))},_autoSize:function(e){if(this._get(e,"autoSize")&&!e.inline){var t,i,a,s,n=new Date(2009,11,20),r=this._get(e,"dateFormat");r.match(/[DM]/)&&(t=function(e){for(i=0,a=0,s=0;e.length>s;s++)e[s].length>i&&(i=e[s].length,a=s);return a},n.setMonth(t(this._get(e,r.match(/MM/)?"monthNames":"monthNamesShort"))),n.setDate(t(this._get(e,r.match(/DD/)?"dayNames":"dayNamesShort"))+20-n.getDay())),e.input.attr("size",this._formatDate(e,n).length)}},_inlineDatepicker:function(t,i){var a=e(t);a.hasClass(this.markerClassName)||(a.addClass(this.markerClassName).append(i.dpDiv),e.data(t,r,i),this._setDate(i,this._getDefaultDate(i),!0),this._updateDatepicker(i),this._updateAlternate(i),i.settings.disabled&&this._disableDatepicker(t),i.dpDiv.css("display","block"))},_dialogDatepicker:function(t,i,a,n,o){var u,c,h,l,d,p=this._dialogInst;return p||(this.uuid+=1,u="dp"+this.uuid,this._dialogInput=e(""),this._dialogInput.keydown(this._doKeyDown),e("body").append(this._dialogInput),p=this._dialogInst=this._newInst(this._dialogInput,!1),p.settings={},e.data(this._dialogInput[0],r,p)),s(p.settings,n||{}),i=i&&i.constructor===Date?this._formatDate(p,i):i,this._dialogInput.val(i),this._pos=o?o.length?o:[o.pageX,o.pageY]:null,this._pos||(c=document.documentElement.clientWidth,h=document.documentElement.clientHeight,l=document.documentElement.scrollLeft||document.body.scrollLeft,d=document.documentElement.scrollTop||document.body.scrollTop,this._pos=[c/2-100+l,h/2-150+d]),this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px"),p.settings.onSelect=a,this._inDialog=!0,this.dpDiv.addClass(this._dialogClass),this._showDatepicker(this._dialogInput[0]),e.blockUI&&e.blockUI(this.dpDiv),e.data(this._dialogInput[0],r,p),this},_destroyDatepicker:function(t){var i,a=e(t),s=e.data(t,r);a.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),e.removeData(t,r),"input"===i?(s.append.remove(),s.trigger.remove(),a.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)):("div"===i||"span"===i)&&a.removeClass(this.markerClassName).empty())},_enableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!1,n.trigger.filter("button").each(function(){this.disabled=!1}).end().filter("img").css({opacity:"1.0",cursor:""})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().removeClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!1)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}))},_disableDatepicker:function(t){var i,a,s=e(t),n=e.data(t,r);s.hasClass(this.markerClassName)&&(i=t.nodeName.toLowerCase(),"input"===i?(t.disabled=!0,n.trigger.filter("button").each(function(){this.disabled=!0}).end().filter("img").css({opacity:"0.5",cursor:"default"})):("div"===i||"span"===i)&&(a=s.children("."+this._inlineClass),a.children().addClass("ui-state-disabled"),a.find("select.ui-datepicker-month, select.ui-datepicker-year").prop("disabled",!0)),this._disabledInputs=e.map(this._disabledInputs,function(e){return e===t?null:e}),this._disabledInputs[this._disabledInputs.length]=t)},_isDisabledDatepicker:function(e){if(!e)return!1;for(var t=0;this._disabledInputs.length>t;t++)if(this._disabledInputs[t]===e)return!0;return!1},_getInst:function(t){try{return e.data(t,r)}catch(i){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(i,a,n){var r,o,u,c,h=this._getInst(i);return 2===arguments.length&&"string"==typeof a?"defaults"===a?e.extend({},e.datepicker._defaults):h?"all"===a?e.extend({},h.settings):this._get(h,a):null:(r=a||{},"string"==typeof a&&(r={},r[a]=n),h&&(this._curInst===h&&this._hideDatepicker(),o=this._getDateDatepicker(i,!0),u=this._getMinMaxDate(h,"min"),c=this._getMinMaxDate(h,"max"),s(h.settings,r),null!==u&&r.dateFormat!==t&&r.minDate===t&&(h.settings.minDate=this._formatDate(h,u)),null!==c&&r.dateFormat!==t&&r.maxDate===t&&(h.settings.maxDate=this._formatDate(h,c)),"disabled"in r&&(r.disabled?this._disableDatepicker(i):this._enableDatepicker(i)),this._attachments(e(i),h),this._autoSize(h),this._setDate(h,o),this._updateAlternate(h),this._updateDatepicker(h)),t)},_changeDatepicker:function(e,t,i){this._optionDatepicker(e,t,i)},_refreshDatepicker:function(e){var t=this._getInst(e);t&&this._updateDatepicker(t)},_setDateDatepicker:function(e,t){var i=this._getInst(e);i&&(this._setDate(i,t),this._updateDatepicker(i),this._updateAlternate(i))},_getDateDatepicker:function(e,t){var i=this._getInst(e);return i&&!i.inline&&this._setDateFromField(i,t),i?this._getDate(i):null},_doKeyDown:function(t){var i,a,s,n=e.datepicker._getInst(t.target),r=!0,o=n.dpDiv.is(".ui-datepicker-rtl");if(n._keyEvent=!0,e.datepicker._datepickerShowing)switch(t.keyCode){case 9:e.datepicker._hideDatepicker(),r=!1;break;case 13:return s=e("td."+e.datepicker._dayOverClass+":not(."+e.datepicker._currentClass+")",n.dpDiv),s[0]&&e.datepicker._selectDay(t.target,n.selectedMonth,n.selectedYear,s[0]),i=e.datepicker._get(n,"onSelect"),i?(a=e.datepicker._formatDate(n),i.apply(n.input?n.input[0]:null,[a,n])):e.datepicker._hideDatepicker(),!1;case 27:e.datepicker._hideDatepicker();break;case 33:e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 34:e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 35:(t.ctrlKey||t.metaKey)&&e.datepicker._clearDate(t.target),r=t.ctrlKey||t.metaKey;break;case 36:(t.ctrlKey||t.metaKey)&&e.datepicker._gotoToday(t.target),r=t.ctrlKey||t.metaKey;break;case 37:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?1:-1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?-e.datepicker._get(n,"stepBigMonths"):-e.datepicker._get(n,"stepMonths"),"M");break;case 38:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,-7,"D"),r=t.ctrlKey||t.metaKey;break;case 39:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,o?-1:1,"D"),r=t.ctrlKey||t.metaKey,t.originalEvent.altKey&&e.datepicker._adjustDate(t.target,t.ctrlKey?+e.datepicker._get(n,"stepBigMonths"):+e.datepicker._get(n,"stepMonths"),"M");break;case 40:(t.ctrlKey||t.metaKey)&&e.datepicker._adjustDate(t.target,7,"D"),r=t.ctrlKey||t.metaKey;break;default:r=!1}else 36===t.keyCode&&t.ctrlKey?e.datepicker._showDatepicker(this):r=!1;r&&(t.preventDefault(),t.stopPropagation())},_doKeyPress:function(i){var a,s,n=e.datepicker._getInst(i.target);return e.datepicker._get(n,"constrainInput")?(a=e.datepicker._possibleChars(e.datepicker._get(n,"dateFormat")),s=String.fromCharCode(null==i.charCode?i.keyCode:i.charCode),i.ctrlKey||i.metaKey||" ">s||!a||a.indexOf(s)>-1):t},_doKeyUp:function(t){var i,a=e.datepicker._getInst(t.target);if(a.input.val()!==a.lastVal)try{i=e.datepicker.parseDate(e.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,e.datepicker._getFormatConfig(a)),i&&(e.datepicker._setDateFromField(a),e.datepicker._updateAlternate(a),e.datepicker._updateDatepicker(a))}catch(s){}return!0},_showDatepicker:function(t){if(t=t.target||t,"input"!==t.nodeName.toLowerCase()&&(t=e("input",t.parentNode)[0]),!e.datepicker._isDisabledDatepicker(t)&&e.datepicker._lastInput!==t){var i,a,n,r,o,u,c;i=e.datepicker._getInst(t),e.datepicker._curInst&&e.datepicker._curInst!==i&&(e.datepicker._curInst.dpDiv.stop(!0,!0),i&&e.datepicker._datepickerShowing&&e.datepicker._hideDatepicker(e.datepicker._curInst.input[0])),a=e.datepicker._get(i,"beforeShow"),n=a?a.apply(t,[t,i]):{},n!==!1&&(s(i.settings,n),i.lastVal=null,e.datepicker._lastInput=t,e.datepicker._setDateFromField(i),e.datepicker._inDialog&&(t.value=""),e.datepicker._pos||(e.datepicker._pos=e.datepicker._findPos(t),e.datepicker._pos[1]+=t.offsetHeight),r=!1,e(t).parents().each(function(){return r|="fixed"===e(this).css("position"),!r}),o={left:e.datepicker._pos[0],top:e.datepicker._pos[1]},e.datepicker._pos=null,i.dpDiv.empty(),i.dpDiv.css({position:"absolute",display:"block",top:"-1000px"}),e.datepicker._updateDatepicker(i),o=e.datepicker._checkOffset(i,o,r),i.dpDiv.css({position:e.datepicker._inDialog&&e.blockUI?"static":r?"fixed":"absolute",display:"none",left:o.left+"px",top:o.top+"px"}),i.inline||(u=e.datepicker._get(i,"showAnim"),c=e.datepicker._get(i,"duration"),i.dpDiv.zIndex(e(t).zIndex()+1),e.datepicker._datepickerShowing=!0,e.effects&&e.effects.effect[u]?i.dpDiv.show(u,e.datepicker._get(i,"showOptions"),c):i.dpDiv[u||"show"](u?c:null),e.datepicker._shouldFocusInput(i)&&i.input.focus(),e.datepicker._curInst=i))}},_updateDatepicker:function(t){this.maxRows=4,n=t,t.dpDiv.empty().append(this._generateHTML(t)),this._attachHandlers(t),t.dpDiv.find("."+this._dayOverClass+" a").mouseover();var i,a=this._getNumberOfMonths(t),s=a[1],r=17;t.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width(""),s>1&&t.dpDiv.addClass("ui-datepicker-multi-"+s).css("width",r*s+"em"),t.dpDiv[(1!==a[0]||1!==a[1]?"add":"remove")+"Class"]("ui-datepicker-multi"),t.dpDiv[(this._get(t,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl"),t===e.datepicker._curInst&&e.datepicker._datepickerShowing&&e.datepicker._shouldFocusInput(t)&&t.input.focus(),t.yearshtml&&(i=t.yearshtml,setTimeout(function(){i===t.yearshtml&&t.yearshtml&&t.dpDiv.find("select.ui-datepicker-year:first").replaceWith(t.yearshtml),i=t.yearshtml=null},0))},_shouldFocusInput:function(e){return e.input&&e.input.is(":visible")&&!e.input.is(":disabled")&&!e.input.is(":focus")},_checkOffset:function(t,i,a){var s=t.dpDiv.outerWidth(),n=t.dpDiv.outerHeight(),r=t.input?t.input.outerWidth():0,o=t.input?t.input.outerHeight():0,u=document.documentElement.clientWidth+(a?0:e(document).scrollLeft()),c=document.documentElement.clientHeight+(a?0:e(document).scrollTop());return i.left-=this._get(t,"isRTL")?s-r:0,i.left-=a&&i.left===t.input.offset().left?e(document).scrollLeft():0,i.top-=a&&i.top===t.input.offset().top+o?e(document).scrollTop():0,i.left-=Math.min(i.left,i.left+s>u&&u>s?Math.abs(i.left+s-u):0),i.top-=Math.min(i.top,i.top+n>c&&c>n?Math.abs(n+o):0),i},_findPos:function(t){for(var i,a=this._getInst(t),s=this._get(a,"isRTL");t&&("hidden"===t.type||1!==t.nodeType||e.expr.filters.hidden(t));)t=t[s?"previousSibling":"nextSibling"];return i=e(t).offset(),[i.left,i.top]},_hideDatepicker:function(t){var i,a,s,n,o=this._curInst;!o||t&&o!==e.data(t,r)||this._datepickerShowing&&(i=this._get(o,"showAnim"),a=this._get(o,"duration"),s=function(){e.datepicker._tidyDialog(o)},e.effects&&(e.effects.effect[i]||e.effects[i])?o.dpDiv.hide(i,e.datepicker._get(o,"showOptions"),a,s):o.dpDiv["slideDown"===i?"slideUp":"fadeIn"===i?"fadeOut":"hide"](i?a:null,s),i||s(),this._datepickerShowing=!1,n=this._get(o,"onClose"),n&&n.apply(o.input?o.input[0]:null,[o.input?o.input.val():"",o]),this._lastInput=null,this._inDialog&&(this._dialogInput.css({position:"absolute",left:"0",top:"-100px"}),e.blockUI&&(e.unblockUI(),e("body").append(this.dpDiv))),this._inDialog=!1)},_tidyDialog:function(e){e.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(t){if(e.datepicker._curInst){var i=e(t.target),a=e.datepicker._getInst(i[0]);(i[0].id!==e.datepicker._mainDivId&&0===i.parents("#"+e.datepicker._mainDivId).length&&!i.hasClass(e.datepicker.markerClassName)&&!i.closest("."+e.datepicker._triggerClass).length&&e.datepicker._datepickerShowing&&(!e.datepicker._inDialog||!e.blockUI)||i.hasClass(e.datepicker.markerClassName)&&e.datepicker._curInst!==a)&&e.datepicker._hideDatepicker()}},_adjustDate:function(t,i,a){var s=e(t),n=this._getInst(s[0]);this._isDisabledDatepicker(s[0])||(this._adjustInstDate(n,i+("M"===a?this._get(n,"showCurrentAtPos"):0),a),this._updateDatepicker(n))},_gotoToday:function(t){var i,a=e(t),s=this._getInst(a[0]);this._get(s,"gotoCurrent")&&s.currentDay?(s.selectedDay=s.currentDay,s.drawMonth=s.selectedMonth=s.currentMonth,s.drawYear=s.selectedYear=s.currentYear):(i=new Date,s.selectedDay=i.getDate(),s.drawMonth=s.selectedMonth=i.getMonth(),s.drawYear=s.selectedYear=i.getFullYear()),this._notifyChange(s),this._adjustDate(a)},_selectMonthYear:function(t,i,a){var s=e(t),n=this._getInst(s[0]);n["selected"+("M"===a?"Month":"Year")]=n["draw"+("M"===a?"Month":"Year")]=parseInt(i.options[i.selectedIndex].value,10),this._notifyChange(n),this._adjustDate(s)},_selectDay:function(t,i,a,s){var n,r=e(t);e(s).hasClass(this._unselectableClass)||this._isDisabledDatepicker(r[0])||(n=this._getInst(r[0]),n.selectedDay=n.currentDay=e("a",s).html(),n.selectedMonth=n.currentMonth=i,n.selectedYear=n.currentYear=a,this._selectDate(t,this._formatDate(n,n.currentDay,n.currentMonth,n.currentYear)))},_clearDate:function(t){var i=e(t);this._selectDate(i,"")},_selectDate:function(t,i){var a,s=e(t),n=this._getInst(s[0]);i=null!=i?i:this._formatDate(n),n.input&&n.input.val(i),this._updateAlternate(n),a=this._get(n,"onSelect"),a?a.apply(n.input?n.input[0]:null,[i,n]):n.input&&n.input.trigger("change"),n.inline?this._updateDatepicker(n):(this._hideDatepicker(),this._lastInput=n.input[0],"object"!=typeof n.input[0]&&n.input.focus(),this._lastInput=null)},_updateAlternate:function(t){var i,a,s,n=this._get(t,"altField");n&&(i=this._get(t,"altFormat")||this._get(t,"dateFormat"),a=this._getDate(t),s=this.formatDate(i,a,this._getFormatConfig(t)),e(n).each(function(){e(this).val(s)}))},noWeekends:function(e){var t=e.getDay();return[t>0&&6>t,""]},iso8601Week:function(e){var t,i=new Date(e.getTime());return i.setDate(i.getDate()+4-(i.getDay()||7)),t=i.getTime(),i.setMonth(0),i.setDate(1),Math.floor(Math.round((t-i)/864e5)/7)+1},parseDate:function(i,a,s){if(null==i||null==a)throw"Invalid arguments";if(a="object"==typeof a?""+a:a+"",""===a)return null;var n,r,o,u,c=0,h=(s?s.shortYearCutoff:null)||this._defaults.shortYearCutoff,l="string"!=typeof h?h:(new Date).getFullYear()%100+parseInt(h,10),d=(s?s.dayNamesShort:null)||this._defaults.dayNamesShort,p=(s?s.dayNames:null)||this._defaults.dayNames,g=(s?s.monthNamesShort:null)||this._defaults.monthNamesShort,m=(s?s.monthNames:null)||this._defaults.monthNames,f=-1,_=-1,v=-1,k=-1,y=!1,b=function(e){var t=i.length>n+1&&i.charAt(n+1)===e;return t&&n++,t},D=function(e){var t=b(e),i="@"===e?14:"!"===e?20:"y"===e&&t?4:"o"===e?3:2,s=RegExp("^\\d{1,"+i+"}"),n=a.substring(c).match(s);if(!n)throw"Missing number at position "+c;return c+=n[0].length,parseInt(n[0],10)},w=function(i,s,n){var r=-1,o=e.map(b(i)?n:s,function(e,t){return[[t,e]]}).sort(function(e,t){return-(e[1].length-t[1].length)});if(e.each(o,function(e,i){var s=i[1];return a.substr(c,s.length).toLowerCase()===s.toLowerCase()?(r=i[0],c+=s.length,!1):t}),-1!==r)return r+1;throw"Unknown name at position "+c},M=function(){if(a.charAt(c)!==i.charAt(n))throw"Unexpected literal at position "+c;c++};for(n=0;i.length>n;n++)if(y)"'"!==i.charAt(n)||b("'")?M():y=!1;else switch(i.charAt(n)){case"d":v=D("d");break;case"D":w("D",d,p);break;case"o":k=D("o");break;case"m":_=D("m");break;case"M":_=w("M",g,m);break;case"y":f=D("y");break;case"@":u=new Date(D("@")),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"!":u=new Date((D("!")-this._ticksTo1970)/1e4),f=u.getFullYear(),_=u.getMonth()+1,v=u.getDate();break;case"'":b("'")?M():y=!0;break;default:M()}if(a.length>c&&(o=a.substr(c),!/^\s+/.test(o)))throw"Extra/unparsed characters found in date: "+o;if(-1===f?f=(new Date).getFullYear():100>f&&(f+=(new Date).getFullYear()-(new Date).getFullYear()%100+(l>=f?0:-100)),k>-1)for(_=1,v=k;;){if(r=this._getDaysInMonth(f,_-1),r>=v)break;_++,v-=r}if(u=this._daylightSavingAdjust(new Date(f,_-1,v)),u.getFullYear()!==f||u.getMonth()+1!==_||u.getDate()!==v)throw"Invalid date";return u},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:1e7*60*60*24*(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925)),formatDate:function(e,t,i){if(!t)return"";var a,s=(i?i.dayNamesShort:null)||this._defaults.dayNamesShort,n=(i?i.dayNames:null)||this._defaults.dayNames,r=(i?i.monthNamesShort:null)||this._defaults.monthNamesShort,o=(i?i.monthNames:null)||this._defaults.monthNames,u=function(t){var i=e.length>a+1&&e.charAt(a+1)===t;return i&&a++,i},c=function(e,t,i){var a=""+t;if(u(e))for(;i>a.length;)a="0"+a;return a},h=function(e,t,i,a){return u(e)?a[t]:i[t]},l="",d=!1;if(t)for(a=0;e.length>a;a++)if(d)"'"!==e.charAt(a)||u("'")?l+=e.charAt(a):d=!1;else switch(e.charAt(a)){case"d":l+=c("d",t.getDate(),2);break;case"D":l+=h("D",t.getDay(),s,n);break;case"o":l+=c("o",Math.round((new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()-new Date(t.getFullYear(),0,0).getTime())/864e5),3);break;case"m":l+=c("m",t.getMonth()+1,2);break;case"M":l+=h("M",t.getMonth(),r,o);break;case"y":l+=u("y")?t.getFullYear():(10>t.getYear()%100?"0":"")+t.getYear()%100;break;case"@":l+=t.getTime();break;case"!":l+=1e4*t.getTime()+this._ticksTo1970;break;case"'":u("'")?l+="'":d=!0;break;default:l+=e.charAt(a)}return l},_possibleChars:function(e){var t,i="",a=!1,s=function(i){var a=e.length>t+1&&e.charAt(t+1)===i;return a&&t++,a};for(t=0;e.length>t;t++)if(a)"'"!==e.charAt(t)||s("'")?i+=e.charAt(t):a=!1;else switch(e.charAt(t)){case"d":case"m":case"y":case"@":i+="0123456789";break;case"D":case"M":return null;case"'":s("'")?i+="'":a=!0;break;default:i+=e.charAt(t)}return i},_get:function(e,i){return e.settings[i]!==t?e.settings[i]:this._defaults[i]},_setDateFromField:function(e,t){if(e.input.val()!==e.lastVal){var i=this._get(e,"dateFormat"),a=e.lastVal=e.input?e.input.val():null,s=this._getDefaultDate(e),n=s,r=this._getFormatConfig(e);try{n=this.parseDate(i,a,r)||s}catch(o){a=t?"":a}e.selectedDay=n.getDate(),e.drawMonth=e.selectedMonth=n.getMonth(),e.drawYear=e.selectedYear=n.getFullYear(),e.currentDay=a?n.getDate():0,e.currentMonth=a?n.getMonth():0,e.currentYear=a?n.getFullYear():0,this._adjustInstDate(e)}},_getDefaultDate:function(e){return this._restrictMinMax(e,this._determineDate(e,this._get(e,"defaultDate"),new Date))},_determineDate:function(t,i,a){var s=function(e){var t=new Date;return t.setDate(t.getDate()+e),t},n=function(i){try{return e.datepicker.parseDate(e.datepicker._get(t,"dateFormat"),i,e.datepicker._getFormatConfig(t))}catch(a){}for(var s=(i.toLowerCase().match(/^c/)?e.datepicker._getDate(t):null)||new Date,n=s.getFullYear(),r=s.getMonth(),o=s.getDate(),u=/([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,c=u.exec(i);c;){switch(c[2]||"d"){case"d":case"D":o+=parseInt(c[1],10);break;case"w":case"W":o+=7*parseInt(c[1],10);break;case"m":case"M":r+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r));break;case"y":case"Y":n+=parseInt(c[1],10),o=Math.min(o,e.datepicker._getDaysInMonth(n,r))}c=u.exec(i)}return new Date(n,r,o)},r=null==i||""===i?a:"string"==typeof i?n(i):"number"==typeof i?isNaN(i)?a:s(i):new Date(i.getTime());return r=r&&"Invalid Date"==""+r?a:r,r&&(r.setHours(0),r.setMinutes(0),r.setSeconds(0),r.setMilliseconds(0)),this._daylightSavingAdjust(r)},_daylightSavingAdjust:function(e){return e?(e.setHours(e.getHours()>12?e.getHours()+2:0),e):null},_setDate:function(e,t,i){var a=!t,s=e.selectedMonth,n=e.selectedYear,r=this._restrictMinMax(e,this._determineDate(e,t,new Date));e.selectedDay=e.currentDay=r.getDate(),e.drawMonth=e.selectedMonth=e.currentMonth=r.getMonth(),e.drawYear=e.selectedYear=e.currentYear=r.getFullYear(),s===e.selectedMonth&&n===e.selectedYear||i||this._notifyChange(e),this._adjustInstDate(e),e.input&&e.input.val(a?"":this._formatDate(e))},_getDate:function(e){var t=!e.currentYear||e.input&&""===e.input.val()?null:this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return t},_attachHandlers:function(t){var i=this._get(t,"stepMonths"),a="#"+t.id.replace(/\\\\/g,"\\");t.dpDiv.find("[data-handler]").map(function(){var t={prev:function(){e.datepicker._adjustDate(a,-i,"M")},next:function(){e.datepicker._adjustDate(a,+i,"M")},hide:function(){e.datepicker._hideDatepicker()},today:function(){e.datepicker._gotoToday(a)},selectDay:function(){return e.datepicker._selectDay(a,+this.getAttribute("data-month"),+this.getAttribute("data-year"),this),!1},selectMonth:function(){return e.datepicker._selectMonthYear(a,this,"M"),!1},selectYear:function(){return e.datepicker._selectMonthYear(a,this,"Y"),!1}};e(this).bind(this.getAttribute("data-event"),t[this.getAttribute("data-handler")])})},_generateHTML:function(e){var t,i,a,s,n,r,o,u,c,h,l,d,p,g,m,f,_,v,k,y,b,D,w,M,C,x,I,N,T,A,E,S,Y,F,P,O,j,K,R,H=new Date,W=this._daylightSavingAdjust(new Date(H.getFullYear(),H.getMonth(),H.getDate())),L=this._get(e,"isRTL"),U=this._get(e,"showButtonPanel"),B=this._get(e,"hideIfNoPrevNext"),z=this._get(e,"navigationAsDateFormat"),q=this._getNumberOfMonths(e),G=this._get(e,"showCurrentAtPos"),J=this._get(e,"stepMonths"),Q=1!==q[0]||1!==q[1],V=this._daylightSavingAdjust(e.currentDay?new Date(e.currentYear,e.currentMonth,e.currentDay):new Date(9999,9,9)),$=this._getMinMaxDate(e,"min"),X=this._getMinMaxDate(e,"max"),Z=e.drawMonth-G,et=e.drawYear;if(0>Z&&(Z+=12,et--),X)for(t=this._daylightSavingAdjust(new Date(X.getFullYear(),X.getMonth()-q[0]*q[1]+1,X.getDate())),t=$&&$>t?$:t;this._daylightSavingAdjust(new Date(et,Z,1))>t;)Z--,0>Z&&(Z=11,et--);for(e.drawMonth=Z,e.drawYear=et,i=this._get(e,"prevText"),i=z?this.formatDate(i,this._daylightSavingAdjust(new Date(et,Z-J,1)),this._getFormatConfig(e)):i,a=this._canAdjustMonth(e,-1,et,Z)?"
    "+i+"":B?"":""+i+"",s=this._get(e,"nextText"),s=z?this.formatDate(s,this._daylightSavingAdjust(new Date(et,Z+J,1)),this._getFormatConfig(e)):s,n=this._canAdjustMonth(e,1,et,Z)?""+s+"":B?"":""+s+"",r=this._get(e,"currentText"),o=this._get(e,"gotoCurrent")&&e.currentDay?V:W,r=z?this.formatDate(r,o,this._getFormatConfig(e)):r,u=e.inline?"":"",c=U?"
    "+(L?u:"")+(this._isInRange(e,o)?"":"")+(L?"":u)+"
    ":"",h=parseInt(this._get(e,"firstDay"),10),h=isNaN(h)?0:h,l=this._get(e,"showWeek"),d=this._get(e,"dayNames"),p=this._get(e,"dayNamesMin"),g=this._get(e,"monthNames"),m=this._get(e,"monthNamesShort"),f=this._get(e,"beforeShowDay"),_=this._get(e,"showOtherMonths"),v=this._get(e,"selectOtherMonths"),k=this._getDefaultDate(e),y="",D=0;q[0]>D;D++){for(w="",this.maxRows=4,M=0;q[1]>M;M++){if(C=this._daylightSavingAdjust(new Date(et,Z,e.selectedDay)),x=" ui-corner-all",I="",Q){if(I+="
    "}for(I+="
    "+(/all|left/.test(x)&&0===D?L?n:a:"")+(/all|right/.test(x)&&0===D?L?a:n:"")+this._generateMonthYearHeader(e,Z,et,$,X,D>0||M>0,g,m)+"
    "+"",N=l?"":"",b=0;7>b;b++)T=(b+h)%7,N+="=5?" class='ui-datepicker-week-end'":"")+">"+""+p[T]+"";for(I+=N+"",A=this._getDaysInMonth(et,Z),et===e.selectedYear&&Z===e.selectedMonth&&(e.selectedDay=Math.min(e.selectedDay,A)),E=(this._getFirstDayOfMonth(et,Z)-h+7)%7,S=Math.ceil((E+A)/7),Y=Q?this.maxRows>S?this.maxRows:S:S,this.maxRows=Y,F=this._daylightSavingAdjust(new Date(et,Z,1-E)),P=0;Y>P;P++){for(I+="",O=l?"":"",b=0;7>b;b++)j=f?f.apply(e.input?e.input[0]:null,[F]):[!0,""],K=F.getMonth()!==Z,R=K&&!v||!j[0]||$&&$>F||X&&F>X,O+="",F.setDate(F.getDate()+1),F=this._daylightSavingAdjust(F);I+=O+""}Z++,Z>11&&(Z=0,et++),I+="
    "+this._get(e,"weekHeader")+"
    "+this._get(e,"calculateWeek")(F)+""+(K&&!_?" ":R?""+F.getDate()+"":""+F.getDate()+"")+"
    "+(Q?"
    "+(q[0]>0&&M===q[1]-1?"
    ":""):""),w+=I}y+=w}return y+=c,e._keyEvent=!1,y},_generateMonthYearHeader:function(e,t,i,a,s,n,r,o){var u,c,h,l,d,p,g,m,f=this._get(e,"changeMonth"),_=this._get(e,"changeYear"),v=this._get(e,"showMonthAfterYear"),k="
    ",y="";if(n||!f)y+=""+r[t]+"";else{for(u=a&&a.getFullYear()===i,c=s&&s.getFullYear()===i,y+=""}if(v||(k+=y+(!n&&f&&_?"":" ")),!e.yearshtml)if(e.yearshtml="",n||!_)k+=""+i+"";else{for(l=this._get(e,"yearRange").split(":"),d=(new Date).getFullYear(),p=function(e){var t=e.match(/c[+\-].*/)?i+parseInt(e.substring(1),10):e.match(/[+\-].*/)?d+parseInt(e,10):parseInt(e,10); -return isNaN(t)?d:t},g=p(l[0]),m=Math.max(g,p(l[1]||"")),g=a?Math.max(g,a.getFullYear()):g,m=s?Math.min(m,s.getFullYear()):m,e.yearshtml+="",k+=e.yearshtml,e.yearshtml=null}return k+=this._get(e,"yearSuffix"),v&&(k+=(!n&&f&&_?"":" ")+y),k+="
    "},_adjustInstDate:function(e,t,i){var a=e.drawYear+("Y"===i?t:0),s=e.drawMonth+("M"===i?t:0),n=Math.min(e.selectedDay,this._getDaysInMonth(a,s))+("D"===i?t:0),r=this._restrictMinMax(e,this._daylightSavingAdjust(new Date(a,s,n)));e.selectedDay=r.getDate(),e.drawMonth=e.selectedMonth=r.getMonth(),e.drawYear=e.selectedYear=r.getFullYear(),("M"===i||"Y"===i)&&this._notifyChange(e)},_restrictMinMax:function(e,t){var i=this._getMinMaxDate(e,"min"),a=this._getMinMaxDate(e,"max"),s=i&&i>t?i:t;return a&&s>a?a:s},_notifyChange:function(e){var t=this._get(e,"onChangeMonthYear");t&&t.apply(e.input?e.input[0]:null,[e.selectedYear,e.selectedMonth+1,e])},_getNumberOfMonths:function(e){var t=this._get(e,"numberOfMonths");return null==t?[1,1]:"number"==typeof t?[1,t]:t},_getMinMaxDate:function(e,t){return this._determineDate(e,this._get(e,t+"Date"),null)},_getDaysInMonth:function(e,t){return 32-this._daylightSavingAdjust(new Date(e,t,32)).getDate()},_getFirstDayOfMonth:function(e,t){return new Date(e,t,1).getDay()},_canAdjustMonth:function(e,t,i,a){var s=this._getNumberOfMonths(e),n=this._daylightSavingAdjust(new Date(i,a+(0>t?t:s[0]*s[1]),1));return 0>t&&n.setDate(this._getDaysInMonth(n.getFullYear(),n.getMonth())),this._isInRange(e,n)},_isInRange:function(e,t){var i,a,s=this._getMinMaxDate(e,"min"),n=this._getMinMaxDate(e,"max"),r=null,o=null,u=this._get(e,"yearRange");return u&&(i=u.split(":"),a=(new Date).getFullYear(),r=parseInt(i[0],10),o=parseInt(i[1],10),i[0].match(/[+\-].*/)&&(r+=a),i[1].match(/[+\-].*/)&&(o+=a)),(!s||t.getTime()>=s.getTime())&&(!n||t.getTime()<=n.getTime())&&(!r||t.getFullYear()>=r)&&(!o||o>=t.getFullYear())},_getFormatConfig:function(e){var t=this._get(e,"shortYearCutoff");return t="string"!=typeof t?t:(new Date).getFullYear()%100+parseInt(t,10),{shortYearCutoff:t,dayNamesShort:this._get(e,"dayNamesShort"),dayNames:this._get(e,"dayNames"),monthNamesShort:this._get(e,"monthNamesShort"),monthNames:this._get(e,"monthNames")}},_formatDate:function(e,t,i,a){t||(e.currentDay=e.selectedDay,e.currentMonth=e.selectedMonth,e.currentYear=e.selectedYear);var s=t?"object"==typeof t?t:this._daylightSavingAdjust(new Date(a,i,t)):this._daylightSavingAdjust(new Date(e.currentYear,e.currentMonth,e.currentDay));return this.formatDate(this._get(e,"dateFormat"),s,this._getFormatConfig(e))}}),e.fn.datepicker=function(t){if(!this.length)return this;e.datepicker.initialized||(e(document).mousedown(e.datepicker._checkExternalClick),e.datepicker.initialized=!0),0===e("#"+e.datepicker._mainDivId).length&&e("body").append(e.datepicker.dpDiv);var i=Array.prototype.slice.call(arguments,1);return"string"!=typeof t||"isDisabled"!==t&&"getDate"!==t&&"widget"!==t?"option"===t&&2===arguments.length&&"string"==typeof arguments[1]?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i)):this.each(function(){"string"==typeof t?e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this].concat(i)):e.datepicker._attachDatepicker(this,t)}):e.datepicker["_"+t+"Datepicker"].apply(e.datepicker,[this[0]].concat(i))},e.datepicker=new i,e.datepicker.initialized=!1,e.datepicker.uuid=(new Date).getTime(),e.datepicker.version="1.10.4"})(jQuery);(function(t){t.widget("ui.menu",{version:"1.10.4",defaultElement:"
      ",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content ui-corner-all").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}).bind("click"+this.eventNamespace,t.proxy(function(t){this.options.disabled&&t.preventDefault()},this)),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item > a":function(t){t.preventDefault()},"click .ui-state-disabled > a":function(t){t.preventDefault()},"click .ui-menu-item:has(a)":function(e){var i=t(e.target).closest(".ui-menu-item");!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&t(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){var i=t(e.currentTarget);i.siblings().children(".ui-state-active").removeClass("ui-state-active"),this.focus(e,i)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.children(".ui-menu-item").eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){t.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){t(e.target).closest(".ui-menu").length||this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").children("a").removeUniqueId().removeClass("ui-corner-all ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var e=t(this);e.data("ui-menu-submenu-carat")&&e.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(e){function i(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var s,n,a,o,r,l=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:l=!1,n=this.previousFilter||"",a=String.fromCharCode(e.keyCode),o=!1,clearTimeout(this.filterTimer),a===n?o=!0:a=n+a,r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())}),s=o&&-1!==s.index(this.active.next())?this.active.nextAll(".ui-menu-item"):s,s.length||(a=String.fromCharCode(e.keyCode),r=RegExp("^"+i(a),"i"),s=this.activeMenu.children(".ui-menu-item").filter(function(){return r.test(t(this).children("a").text())})),s.length?(this.focus(e,s),s.length>1?(this.previousFilter=a,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}l&&e.preventDefault()},_activate:function(t){this.active.is(".ui-state-disabled")||(this.active.children("a[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i=this.options.icons.submenu,s=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-corner-all").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),s=e.prev("a"),n=t("").addClass("ui-menu-icon ui-icon "+i).data("ui-menu-submenu-carat",!0);s.attr("aria-haspopup","true").prepend(n),e.attr("aria-labelledby",s.attr("id"))}),e=s.add(this.element),e.children(":not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","presentation").children("a").uniqueId().addClass("ui-corner-all").attr({tabIndex:-1,role:this._itemRole()}),e.children(":not(.ui-menu-item)").each(function(){var e=t(this);/[^\-\u2014\u2013\s]/.test(e.text())||e.addClass("ui-widget-content ui-menu-divider")}),e.children(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){"icons"===t&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(e.submenu),this._super(t,e)},focus:function(t,e){var i,s;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children("a").addClass("ui-state-focus"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),this.active.parent().closest(".ui-menu-item").children("a:first").addClass("ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,a,o,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,a=this.activeMenu.scrollTop(),o=this.activeMenu.height(),r=e.height(),0>n?this.activeMenu.scrollTop(a+n):n+r>o&&this.activeMenu.scrollTop(a+n-o+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this.active.children("a").removeClass("ui-state-focus"),this.active=null,this._trigger("blur",t,{item:this.active}))},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find("a.ui-state-active").removeClass("ui-state-active")},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").children(".ui-menu-item").first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.children(".ui-menu-item")[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item")[this.active?"last":"first"]())),undefined):(this.next(e),undefined)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.children(".ui-menu-item").first())),undefined):(this.next(e),undefined)},_hasScroll:function(){return this.element.outerHeight()>label: Widget +//>>group: Core +//>>description: Provides a factory for creating stateful widgets with a common API. +//>>docs: http://api.jqueryui.com/jQuery.widget/ +//>>demos: http://jqueryui.com/widget/ + + + +var widgetUuid = 0; +var widgetSlice = Array.prototype.slice; + +$.cleanData = ( function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // Http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; +} )( $.cleanData ); + +$.widget = function( name, base, prototype ) { + var existingConstructor, constructor, basePrototype; + + // ProxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + var proxiedPrototype = {}; + + var namespace = name.split( "." )[ 0 ]; + name = name.split( "." )[ 1 ]; + var fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + if ( $.isArray( prototype ) ) { + prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); + } + + // Create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + + // Allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // Allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + // Extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + + // Copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + + // Track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + } ); + + basePrototype = new base(); + + // We need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = ( function() { + function _super() { + return base.prototype[ prop ].apply( this, arguments ); + } + + function _superApply( args ) { + return base.prototype[ prop ].apply( this, args ); + } + + return function() { + var __super = this._super; + var __superApply = this._superApply; + var returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + } )(); + } ); + constructor.prototype = $.widget.extend( basePrototype, { + + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + } ); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // Redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, + child._proto ); + } ); + + // Remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; +}; + +$.widget.extend = function( target ) { + var input = widgetSlice.call( arguments, 1 ); + var inputIndex = 0; + var inputLength = input.length; + var key; + var value; + + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string"; + var args = widgetSlice.call( arguments, 1 ); + var returnValue = this; + + if ( isMethodCall ) { + this.each( function() { + var methodValue; + var instance = $.data( this, fullName ); + + if ( options === "instance" ) { + returnValue = instance; + return false; + } + + if ( !instance ) { + return $.error( "cannot call methods on " + name + + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + + if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + + " widget instance" ); + } + + methodValue = instance[ options ].apply( instance, args ); + + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + } ); + } else { + + // Allow multiple hashes to be passed on init + if ( args.length ) { + options = $.widget.extend.apply( null, [ options ].concat( args ) ); + } + + this.each( function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + } ); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "
      ", + + options: { + classes: {}, + disabled: false, + + // Callbacks + create: null + }, + + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widgetUuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + this.classesElementLookup = {}; + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + } ); + this.document = $( element.style ? + + // Element within the document + element.ownerDocument : + + // Element is window or document + element.document || element ); + this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + + if ( this.options.disabled ) { + this._setOptionDisabled( this.options.disabled ); + } + + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + + _getCreateOptions: function() { + return {}; + }, + + _getCreateEventData: $.noop, + + _create: $.noop, + + _init: $.noop, + + destroy: function() { + var that = this; + + this._destroy(); + $.each( this.classesElementLookup, function( key, value ) { + that._removeClass( value, key ); + } ); + + // We can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .off( this.eventNamespace ) + .removeData( this.widgetFullName ); + this.widget() + .off( this.eventNamespace ) + .removeAttr( "aria-disabled" ); + + // Clean up events and states + this.bindings.off( this.eventNamespace ); + }, + + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + var parts; + var curOption; + var i; + + if ( arguments.length === 0 ) { + + // Don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + + // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + + _setOption: function( key, value ) { + if ( key === "classes" ) { + this._setOptionClasses( value ); + } + + this.options[ key ] = value; + + if ( key === "disabled" ) { + this._setOptionDisabled( value ); + } + + return this; + }, + + _setOptionClasses: function( value ) { + var classKey, elements, currentElements; + + for ( classKey in value ) { + currentElements = this.classesElementLookup[ classKey ]; + if ( value[ classKey ] === this.options.classes[ classKey ] || + !currentElements || + !currentElements.length ) { + continue; + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $( currentElements.get() ); + this._removeClass( currentElements, classKey ); + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( this._classes( { + element: elements, + keys: classKey, + classes: value, + add: true + } ) ); + } + }, + + _setOptionDisabled: function( value ) { + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this._removeClass( this.hoverable, null, "ui-state-hover" ); + this._removeClass( this.focusable, null, "ui-state-focus" ); + } + }, + + enable: function() { + return this._setOptions( { disabled: false } ); + }, + + disable: function() { + return this._setOptions( { disabled: true } ); + }, + + _classes: function( options ) { + var full = []; + var that = this; + + options = $.extend( { + element: this.element, + classes: this.options.classes || {} + }, options ); + + function processClassString( classes, checkOption ) { + var current, i; + for ( i = 0; i < classes.length; i++ ) { + current = that.classesElementLookup[ classes[ i ] ] || $(); + if ( options.add ) { + current = $( $.unique( current.get().concat( options.element.get() ) ) ); + } else { + current = $( current.not( options.element ).get() ); + } + that.classesElementLookup[ classes[ i ] ] = current; + full.push( classes[ i ] ); + if ( checkOption && options.classes[ classes[ i ] ] ) { + full.push( options.classes[ classes[ i ] ] ); + } + } + } + + if ( options.keys ) { + processClassString( options.keys.match( /\S+/g ) || [], true ); + } + if ( options.extra ) { + processClassString( options.extra.match( /\S+/g ) || [] ); + } + + return full.join( " " ); + }, + + _removeClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, false ); + }, + + _addClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, true ); + }, + + _toggleClass: function( element, keys, extra, add ) { + add = ( typeof add === "boolean" ) ? add : extra; + var shift = ( typeof element === "string" || element === null ), + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + }; + options.element.toggleClass( this._classes( options ), add ); + return this; + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement; + var instance = this; + + // No suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // No element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + + // Allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // Copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ); + var eventName = match[ 1 ] + instance.eventNamespace; + var selector = match[ 2 ]; + + if ( selector ) { + delegateElement.on( eventName, selector, handlerProxy ); + } else { + element.on( eventName, handlerProxy ); + } + } ); + }, + + _off: function( element, eventName ) { + eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.off( eventName ).off( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); + }, + mouseleave: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); + } + } ); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); + }, + focusout: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); + } + } ); + }, + + _trigger: function( type, event, data ) { + var prop, orig; + var callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + + // The original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // Copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + + var hasOptions; + var effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + + if ( options.delay ) { + element.delay( options.delay ); + } + + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue( function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + } ); + } + }; +} ); + +var widget = $.widget; + + +/*! + * jQuery UI Position 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + +//>>label: Position +//>>group: Core +//>>description: Positions elements relative to other elements. +//>>docs: http://api.jqueryui.com/position/ +//>>demos: http://jqueryui.com/position/ + + +( function() { +var cachedScrollbarWidth, supportsOffsetFractions, + max = Math.max, + abs = Math.abs, + round = Math.round, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +// Support: IE <=9 only +supportsOffsetFractions = function() { + var element = $( "
      " ) + .css( "position", "absolute" ) + .appendTo( "body" ) + .offset( { + top: 1.5, + left: 1.5 + } ), + support = element.offset().top === 1.5; + + element.remove(); + + supportsOffsetFractions = function() { + return support; + }; + + return support; +}; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[ 0 ]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "
      " + + "
      " ), + innerDiv = div.children()[ 0 ]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[ 0 ].clientWidth; + } + + div.remove(); + + return ( cachedScrollbarWidth = w1 - w2 ); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[ 0 ] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, + hasOffset = !isWindow && !isDocument; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: withinElement.outerWidth(), + height: withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // Make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[ 0 ].preventDefault ) { + + // Force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + + // Clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // Force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1 ) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // Calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // Reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + } ); + + // Normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each( function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + // If the browser doesn't support fractions, then round for consistent results + if ( !supportsOffsetFractions() ) { + position.left = round( position.left ); + position.top = round( position.top ); + } + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + } ); + } + } ); + + if ( options.using ) { + + // Adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + } ); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // Element is wider than within + if ( data.collisionWidth > outerWidth ) { + + // Element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - + withinOffset; + position.left += overLeft - newOverRight; + + // Element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + + // Element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + + // Too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + + // Too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + + // Adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // Element is taller than within + if ( data.collisionHeight > outerHeight ) { + + // Element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - + withinOffset; + position.top += overTop - newOverBottom; + + // Element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + + // Element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + + // Too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + + // Too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + + // Adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - + outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - + outerHeight - withinOffset; + if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + + offset - offsetTop; + if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +} )(); + +var position = $.ui.position; + + +/*! + * jQuery UI :data 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: :data Selector +//>>group: Core +//>>description: Selects elements which have data stored under the specified key. +//>>docs: http://api.jqueryui.com/data-selector/ + + +var data = $.extend( $.expr[ ":" ], { + data: $.expr.createPseudo ? + $.expr.createPseudo( function( dataName ) { + return function( elem ) { + return !!$.data( elem, dataName ); + }; + } ) : + + // Support: jQuery <1.8 + function( elem, i, match ) { + return !!$.data( elem, match[ 3 ] ); + } +} ); + +/*! + * jQuery UI Disable Selection 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: disableSelection +//>>group: Core +//>>description: Disable selection of text content within the set of matched elements. +//>>docs: http://api.jqueryui.com/disableSelection/ + +// This file is deprecated + + +var disableSelection = $.fn.extend( { + disableSelection: ( function() { + var eventType = "onselectstart" in document.createElement( "div" ) ? + "selectstart" : + "mousedown"; + + return function() { + return this.on( eventType + ".ui-disableSelection", function( event ) { + event.preventDefault(); + } ); + }; + } )(), + + enableSelection: function() { + return this.off( ".ui-disableSelection" ); + } +} ); + + +/*! + * jQuery UI Keycode 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Keycode +//>>group: Core +//>>description: Provide keycodes as keynames +//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/ + + +var keycode = $.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 +}; + + +/*! + * jQuery UI Scroll Parent 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: scrollParent +//>>group: Core +//>>description: Get the closest ancestor element that is scrollable. +//>>docs: http://api.jqueryui.com/scrollParent/ + + + +var scrollParent = $.fn.scrollParent = function( includeHidden ) { + var position = this.css( "position" ), + excludeStaticParent = position === "absolute", + overflowRegex = includeHidden ? /(auto|scroll|hidden)/ : /(auto|scroll)/, + scrollParent = this.parents().filter( function() { + var parent = $( this ); + if ( excludeStaticParent && parent.css( "position" ) === "static" ) { + return false; + } + return overflowRegex.test( parent.css( "overflow" ) + parent.css( "overflow-y" ) + + parent.css( "overflow-x" ) ); + } ).eq( 0 ); + + return position === "fixed" || !scrollParent.length ? + $( this[ 0 ].ownerDocument || document ) : + scrollParent; +}; + + +/*! + * jQuery UI Unique ID 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: uniqueId +//>>group: Core +//>>description: Functions to generate and remove uniqueId's +//>>docs: http://api.jqueryui.com/uniqueId/ + + + +var uniqueId = $.fn.extend( { + uniqueId: ( function() { + var uuid = 0; + + return function() { + return this.each( function() { + if ( !this.id ) { + this.id = "ui-id-" + ( ++uuid ); + } + } ); + }; + } )(), + + removeUniqueId: function() { + return this.each( function() { + if ( /^ui-id-\d+$/.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + } ); + } +} ); + + + + +// This file is deprecated +var ie = $.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() ); + +/*! + * jQuery UI Mouse 1.12.0 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Mouse +//>>group: Widgets +//>>description: Abstracts mouse-based interactions to assist in creating certain widgets. +//>>docs: http://api.jqueryui.com/mouse/ + + + +var mouseHandled = false; +$( document ).on( "mouseup", function() { + mouseHandled = false; +} ); + +var widgetsMouse = $.widget( "ui.mouse", { + version: "1.12.0", + options: { + cancel: "input, textarea, button, select, option", + distance: 1, + delay: 0 + }, + _mouseInit: function() { + var that = this; + + this.element + .on( "mousedown." + this.widgetName, function( event ) { + return that._mouseDown( event ); + } ) + .on( "click." + this.widgetName, function( event ) { + if ( true === $.data( event.target, that.widgetName + ".preventClickEvent" ) ) { + $.removeData( event.target, that.widgetName + ".preventClickEvent" ); + event.stopImmediatePropagation(); + return false; + } + } ); + + this.started = false; + }, + + // TODO: make sure destroying one instance of mouse doesn't mess with + // other instances of mouse + _mouseDestroy: function() { + this.element.off( "." + this.widgetName ); + if ( this._mouseMoveDelegate ) { + this.document + .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); + } + }, + + _mouseDown: function( event ) { + + // don't let more than one widget handle mouseStart + if ( mouseHandled ) { + return; + } + + this._mouseMoved = false; + + // We may have missed mouseup (out of window) + ( this._mouseStarted && this._mouseUp( event ) ); + + this._mouseDownEvent = event; + + var that = this, + btnIsLeft = ( event.which === 1 ), + + // event.target.nodeName works around a bug in IE 8 with + // disabled inputs (#7620) + elIsCancel = ( typeof this.options.cancel === "string" && event.target.nodeName ? + $( event.target ).closest( this.options.cancel ).length : false ); + if ( !btnIsLeft || elIsCancel || !this._mouseCapture( event ) ) { + return true; + } + + this.mouseDelayMet = !this.options.delay; + if ( !this.mouseDelayMet ) { + this._mouseDelayTimer = setTimeout( function() { + that.mouseDelayMet = true; + }, this.options.delay ); + } + + if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { + this._mouseStarted = ( this._mouseStart( event ) !== false ); + if ( !this._mouseStarted ) { + event.preventDefault(); + return true; + } + } + + // Click event may never have fired (Gecko & Opera) + if ( true === $.data( event.target, this.widgetName + ".preventClickEvent" ) ) { + $.removeData( event.target, this.widgetName + ".preventClickEvent" ); + } + + // These delegates are required to keep context + this._mouseMoveDelegate = function( event ) { + return that._mouseMove( event ); + }; + this._mouseUpDelegate = function( event ) { + return that._mouseUp( event ); + }; + + this.document + .on( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .on( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + event.preventDefault(); + + mouseHandled = true; + return true; + }, + + _mouseMove: function( event ) { + + // Only check for mouseups outside the document if you've moved inside the document + // at least once. This prevents the firing of mouseup in the case of IE<9, which will + // fire a mousemove event if content is placed under the cursor. See #7778 + // Support: IE <9 + if ( this._mouseMoved ) { + + // IE mouseup check - mouseup happened when mouse was out of window + if ( $.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && + !event.button ) { + return this._mouseUp( event ); + + // Iframe mouseup check - mouseup occurred in another document + } else if ( !event.which ) { + + // Support: Safari <=8 - 9 + // Safari sets which to 0 if you press any of the following keys + // during a drag (#14461) + if ( event.originalEvent.altKey || event.originalEvent.ctrlKey || + event.originalEvent.metaKey || event.originalEvent.shiftKey ) { + this.ignoreMissingWhich = true; + } else if ( !this.ignoreMissingWhich ) { + return this._mouseUp( event ); + } + } + } + + if ( event.which || event.button ) { + this._mouseMoved = true; + } + + if ( this._mouseStarted ) { + this._mouseDrag( event ); + return event.preventDefault(); + } + + if ( this._mouseDistanceMet( event ) && this._mouseDelayMet( event ) ) { + this._mouseStarted = + ( this._mouseStart( this._mouseDownEvent, event ) !== false ); + ( this._mouseStarted ? this._mouseDrag( event ) : this._mouseUp( event ) ); + } + + return !this._mouseStarted; + }, + + _mouseUp: function( event ) { + this.document + .off( "mousemove." + this.widgetName, this._mouseMoveDelegate ) + .off( "mouseup." + this.widgetName, this._mouseUpDelegate ); + + if ( this._mouseStarted ) { + this._mouseStarted = false; + + if ( event.target === this._mouseDownEvent.target ) { + $.data( event.target, this.widgetName + ".preventClickEvent", true ); + } + + this._mouseStop( event ); + } + + if ( this._mouseDelayTimer ) { + clearTimeout( this._mouseDelayTimer ); + delete this._mouseDelayTimer; + } + + this.ignoreMissingWhich = false; + mouseHandled = false; + event.preventDefault(); + }, + + _mouseDistanceMet: function( event ) { + return ( Math.max( + Math.abs( this._mouseDownEvent.pageX - event.pageX ), + Math.abs( this._mouseDownEvent.pageY - event.pageY ) + ) >= this.options.distance + ); + }, + + _mouseDelayMet: function( /* event */ ) { + return this.mouseDelayMet; + }, + + // These are placeholder methods, to be overriden by extending plugin + _mouseStart: function( /* event */ ) {}, + _mouseDrag: function( /* event */ ) {}, + _mouseStop: function( /* event */ ) {}, + _mouseCapture: function( /* event */ ) { return true; } +} ); + + + + +// $.ui.plugin is deprecated. Use $.widget() extensions instead. +var plugin = $.ui.plugin = { + add: function( module, option, set ) { + var i, + proto = $.ui[ module ].prototype; + for ( i in set ) { + proto.plugins[ i ] = proto.plugins[ i ] || []; + proto.plugins[ i ].push( [ option, set[ i ] ] ); + } + }, + call: function( instance, name, args, allowDisconnected ) { + var i, + set = instance.plugins[ name ]; + + if ( !set ) { + return; + } + + if ( !allowDisconnected && ( !instance.element[ 0 ].parentNode || + instance.element[ 0 ].parentNode.nodeType === 11 ) ) { + return; + } + + for ( i = 0; i < set.length; i++ ) { + if ( instance.options[ set[ i ][ 0 ] ] ) { + set[ i ][ 1 ].apply( instance.element, args ); + } + } + } +}; + + + +var safeActiveElement = $.ui.safeActiveElement = function( document ) { + var activeElement; + + // Support: IE 9 only + // IE9 throws an "Unspecified error" accessing document.activeElement from an