diff --git a/README.md b/README.md index 5d95bd7..f9c1417 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,39 @@ -The Lounge ----------- +The Lounge for YunoHost +==================== +

+ The Lounge +

+ +[![Linter](https://api.travis-ci.org/Rafi594/thelounge_ynh.svg?branch=master) + +[![Integration level](https://dash.yunohost.org/integration/thelounge.svg)](https://ci-apps.yunohost.org/jenkins/job/thelounge%20%28Community%29/lastBuild/consoleFull) + +[![Install The Lounge with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=thelounge) + +> *This package allow you to install The Lounge quickly and simply on a YunoHost server. +If you don't have YunoHost, please see [here](https://yunohost.org/#/install) to know how to install and enjoy it.* + +Overview +-------- + +The Lounge is a self hosted IRC client. + + +**Shipped version:** Latest stable version + +YunoHost specific features +-------------------------- + +### Multi-users support + +Supported with LDAP. + +### Supported architectures + +- Tested on x86_64 +- Tested on ARM -The Lounge is a web IRC client that you host on your own server. -See https://thelounge.github.io/ diff --git a/check_process b/check_process new file mode 100644 index 0000000..10f4d4b --- /dev/null +++ b/check_process @@ -0,0 +1,41 @@ +# See here for more informations +# https://github.com/YunoHost/package_check#syntax-check_process-file + +# Move this file from check_process.default to check_process when you have filled it. + +;; Test complet + ; Manifest + domain="domain.tld" (DOMAIN) + path="/path" (PATH) + is_public=1 (PUBLIC|public=1|private=0) + password="pass" + port="9009" (PORT) + ; Checks + pkg_linter=1 + setup_sub_dir=1 + setup_root=1 + setup_nourl=0 + setup_private=1 + setup_public=1 + upgrade=1 + backup_restore=0 + multi_instance=1 + incorrect_path=1 + port_already_use=1 + change_url=0 +;;; Levels + Level 1=auto + Level 2=auto + Level 3=auto +# Level 4: + Level 4=1 # https://github.com/Rafi594/thelounge_ynh/blob/master/conf/config.js#L391-L452 +# Level 5: + Level 5=auto + Level 6=auto + Level 7=auto + Level 8=0 + Level 9=0 + Level 10=0 +;;; Options +Email= +Notification=none diff --git a/conf/app.src b/conf/app.src new file mode 100644 index 0000000..b4a1acd --- /dev/null +++ b/conf/app.src @@ -0,0 +1,6 @@ +SOURCE_URL=https://github.com/thelounge/thelounge/archive/v2.7.1.tar.gz +SOURCE_SUM=e7b66fe1dfef4583668ccf36214184f767fa381076ca21b09479b6e59251f81d +SOURCE_SUM_PRG=sha256sum +ARCH_FORMAT=tar.gz +SOURCE_IN_SUBDIR=true +SOURCE_FILENAME= \ No newline at end of file diff --git a/conf/config.js b/conf/config.js index 6057240..71d04d1 100644 --- a/conf/config.js +++ b/conf/config.js @@ -8,7 +8,7 @@ module.exports = { // Set to 'false' to enable users. // // @type boolean - // @default true + // @default false // public: false, @@ -16,10 +16,12 @@ module.exports = { // IP address or hostname for the web server to listen on. // Setting this to undefined will listen on all interfaces. // + // For UNIX domain sockets, use unix:/absolute/path/to/file.sock. + // // @type string // @default undefined // - host: "127.0.0.1", + host: undefined, // // Set the port to listen on. @@ -27,7 +29,7 @@ module.exports = { // @type int // @default 9000 // - port: 9191, + port: 9009, // // Set the local IP to bind to for outgoing connections. Leave to undefined @@ -49,22 +51,12 @@ module.exports = { // // Set the default theme. + // Find out how to add new themes at https://thelounge.github.io/docs/plugins/themes.html // // @type string - // @default "themes/example.css" + // @default "example" // - theme: "themes/morning.css", - - // - // Autoload users - // - // When this setting is enabled, your 'users/' folder will be monitored. This is useful - // if you want to add/remove users while the server is running. - // - // @type boolean - // @default true - // - autoload: true, + theme: "example", // // Prefetch URLs @@ -75,18 +67,35 @@ module.exports = { // @type boolean // @default false // - prefetch: true, + prefetch: false, + + // + // Store and proxy prefetched images and thumbnails. + // This improves security and privacy by not exposing client IP address, + // and always loading images from The Lounge instance and making all assets secure, + // which in result fixes mixed content warnings. + // + // If storage is enabled, The Lounge will fetch and store images and thumbnails + // in the `${THELOUNGE_HOME}/storage` folder. + // + // Images are deleted when they are no longer referenced by any message (controlled by maxHistory), + // and the folder is cleaned up on every The Lounge restart. + // + // @type boolean + // @default false + // + prefetchStorage: false, // // Prefetch URLs Image Preview size limit // // If prefetch is enabled, The Lounge will only display content under the maximum size. - // Default value is 512 (in kB) + // Specified value is in kilobytes. Default value is 2048 kilobytes. // // @type int - // @default 512 + // @default 2048 // - prefetchMaxImageSize: 512, + prefetchMaxImageSize: 2048, // // Display network @@ -109,6 +118,17 @@ module.exports = { // lockNetwork: false, + // + // Hex IP + // + // If enabled, clients' username will be set to their IP encoded has hex. + // This is done to share the real user IP address with the server for host masking purposes. + // + // @type boolean + // @default false + // + useHexIp: false, + // // WEBIRC support // @@ -128,6 +148,19 @@ module.exports = { // @default null webirc: null, + // + // Message logging + // Logging is also controlled per user individually (logs variable) + // Leave the array empty to disable all logging globally + // + // text: Text file per network/channel in user folder + // sqlite: Messages are stored in SQLite, this allows them to be reloaded on server restart + // + // @type array + // @default ["sqlite", "text"] + // + messageStorage: ["sqlite", "text"], + // // Log settings // @@ -152,7 +185,7 @@ module.exports = { // @type string // @default "UTC+00:00" // - timezone: "UTC+00:00" + timezone: "UTC+00:00", }, // @@ -160,11 +193,11 @@ module.exports = { // // Defines the maximum number of history lines that will be kept in // memory per channel/query, in order to reduce the memory usage of - // the server. Negative means unlimited. + // the server. Setting this to -1 will keep unlimited amount. // // @type integer - // @default -1 - maxHistory: -1, + // @default 10000 + maxHistory: 10000, // // Default values for the 'Connect' form. @@ -213,21 +246,35 @@ module.exports = { // tls: true, + // + // Enable certificate verification + // + // If true, the server certificate is verified against + // the list of supplied CAs by your node.js installation. + // + // @type boolean + // @default true + // + rejectUnauthorized: true, + // // Nick // - // @type string - // @default "lounge-user" + // Percent sign (%) will be replaced into a random number from 0 to 9. + // For example, Guest%%% will become Guest123 on page load. // - nick: "lounge-user", + // @type string + // @default "thelounge%%" + // + nick: "thelounge%%", // // Username // // @type string - // @default "lounge-user" + // @default "thelounge" // - username: "lounge-user", + username: "thelounge", // // Real Name @@ -244,7 +291,7 @@ module.exports = { // @type string // @default "#thelounge" // - join: "#thelounge" + join: "#thelounge", }, // @@ -287,9 +334,26 @@ module.exports = { // @example "sslcert/key-cert.pem" // @default "" // - certificate: "" + certificate: "", + + // + // Path to the CA bundle. + // + // @type string + // @example "sslcert/bundle.pem" + // @default "" + // + ca: "", }, + // + // Default quit and part message if none is provided. + // + // @type string + // @default "The Lounge - https://thelounge.chat" + // + leaveMessage: "The Lounge - https://thelounge.chat", + // // Run The Lounge with identd support. // @@ -311,7 +375,7 @@ module.exports = { // @type int // @default 113 // - port: 113 + port: 113, }, // @@ -329,6 +393,30 @@ module.exports = { // @type object // @default {} // + // The authentication process works as follows: + // + // 1. Lounge connects to the LDAP server with its system credentials + // 2. It performs a LDAP search query to find the full DN associated to the + // user requesting to log in. + // 3. Lounge tries to connect a second time, but this time using the user's + // DN and password. Auth is validated iff this connection is successful. + // + // The search query takes a couple of parameters in `searchDN`: + // - a base DN `searchDN/base`. Only children nodes of this DN will be likely + // to be returned; + // - a search scope `searchDN/scope` (see LDAP documentation); + // - the query itself, build as (&(=) ) + // where is the user name provided in the log in request, + // is provided by the config and is a filtering complement + // also given in the config, to filter for instance only for nodes of type + // inetOrgPerson, or whatever LDAP search allows. + // + // Alternatively, you can specify the `bindDN` parameter. This will make the lounge + // ignore searchDN options and assume that the user DN is always: + // ,= + // where is the user name provided in the log in request, and + // and are provided by the config. + // ldap: { // // Enable LDAP user authentication @@ -345,8 +433,9 @@ module.exports = { // url: "ldap://127.0.0.1", + // - // LDAP base dn + // LDAP base dn, alternative to searchDN // // @type string // @@ -359,13 +448,6 @@ module.exports = { // @default "uid" // primaryKey: "uid" - }, - // Enables extra debugging output. Turn this on if you experience - // IRC connection issues and want to file a bug report. - // - // @type boolean - // @default false - // - debug: false, + } }; diff --git a/conf/nginx.conf b/conf/nginx.conf index 9d5cf70..68e1227 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,17 +1,14 @@ -location LOCATIONTOCHANGE { - if ($scheme = http) { - rewrite ^ https://$server_name$request_uri? permanent; - } - proxy_pass http://localhost:9191/; - proxy_http_version 1.1; - proxy_set_header Connection "upgrade"; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header X-Forwarded-For $remote_addr; +location ^~ __PATH__/ { + proxy_pass http://127.0.0.1:__PORT__/; + proxy_http_version 1.1; + proxy_set_header Connection "upgrade"; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header X-Forwarded-For $remote_addr; -# by default nginx times out connections in one minute - proxy_read_timeout 1d; + # by default nginx times out connections in one minute + proxy_read_timeout 1d; -# Include SSOWAT user panel. + # Include SSOWAT user panel. include conf.d/yunohost_panel.conf.inc; more_clear_input_headers 'Accept-Encoding'; } diff --git a/conf/systemd.service b/conf/systemd.service index 950669b..39dc46d 100644 --- a/conf/systemd.service +++ b/conf/systemd.service @@ -1,11 +1,17 @@ +[Unit] +Description=The Lounge IRC client +After=__APP__.service + [Service] -ExecStart=/usr/bin/node /var/www/thelounge/index.js -Restart=always -StandardOutput=syslog -StandardError=syslog -SyslogIdentifier=thelounge -User=thelounge -Environment=NODE_ENV=production +Type=simple +ExecStart=/usr/bin/thelounge start --home /home/yunohost.app/__APP__/ +User=__APP__ +Group=__APP__ +Restart=on-failure +RestartSec=5 +StartLimitInterval=60s +StartLimitBurst=3 [Install] -WantedBy=multi-user.target +WantedBy=default.target + diff --git a/manifest.json b/manifest.json index 6559eff..7415d23 100644 --- a/manifest.json +++ b/manifest.json @@ -1,17 +1,19 @@ { "name": "The Lounge", "id": "thelounge", + "packaging_format": 1, "description": { - "en": "Web IRC client", - "fr": "Client Web IRC" + "en": "The Lounge is a web IRC client.", + "fr": "The Lounge est un client web IRC." }, + "version": "1.0", + "url": "https://thelounge.chat/", + "license": "free", "maintainer": { - "name": "beudbeud", - "email": "beudbeud@beudibox.fr" + "name": "rafi59", + "email": "rafi59_dev@srvmaison.fr.nf", + "url": "http://blog.rafi59.codelib.re/" }, - - "multi_instance": "false", - "previous_maintainers": { "name": "beudbeud", "email": "beudbeud@beudibox.fr" @@ -24,17 +26,16 @@ "nginx", "mysql" ], - "arguments": { "install" : [ { "name": "domain", "type": "domain", "ask": { - "en": "Choose a domain for The Lounge", - "fr": "Choisissez un domaine pour The Lounge" + "en": "Choose a domain name for The Lounge", + "fr": "Choisissez un nom de domaine pour The Lounge" }, - "example": "domain.org" + "example": "example.com" }, { "name": "path", @@ -48,13 +49,14 @@ }, { "name": "is_public", + "type": "boolean", "ask": { - "en": "Is it a public instance ?", - "fr": "Est-ce une instance publique ?" + "en": "Is it a public application?", + "fr": "Est-ce une application publique ?" }, - "choices": ["Yes", "No"], - "default": "No" + "default": true } + ] } } diff --git a/scripts/_common.sh b/scripts/_common.sh new file mode 100644 index 0000000..6dbd7af --- /dev/null +++ b/scripts/_common.sh @@ -0,0 +1,166 @@ +#!/bin/bash + +# INFOS +# n (Node version management) utilise la variable PATH pour stocker le path de la version de node à utiliser. +# C'est ainsi qu'il change de version +# ynh_install_nodejs installe la version de nodejs demandée en argument, avec n +# ynh_use_nodejs active une version de nodejs dans le script courant +# 3 variables sont mises à disposition, et 2 sont stockées dans la config de l'app +# - nodejs_path: Le chemin absolu de cette version de node +# Utilisé pour des appels directs à node. +# - nodejs_version: Simplement le numéro de version de nodejs pour cette application +# - nodejs_use_version: Un alias pour charger une version de node dans le shell courant. +# Utilisé pour démarrer un service ou un script qui utilise node ou npm +# Dans ce cas, c'est $PATH qui contient le chemin de la version de node. Il doit être propagé sur les autres shell si nécessaire. + +n_install_dir="/opt/node_n" +node_version_path="/opt/node_n/n/versions/node" +# N_PREFIX est le dossier de n, il doit être chargé dans les variables d'environnement pour n. +export N_PREFIX="$n_install_dir" + +ynh_install_n () { + echo "Installation of N - Node.js version management" >&2 + # Build an app.src for n + mkdir -p "../conf" + echo "SOURCE_URL=https://github.com/tj/n/archive/v2.1.7.tar.gz +SOURCE_SUM=2ba3c9d4dd3c7e38885b37e02337906a1ee91febe6d5c9159d89a9050f2eea8f" > "../conf/n.src" + # Download and extract n + ynh_setup_source "$n_install_dir/git" n + # Install n + (cd "$n_install_dir/git" + PREFIX=$N_PREFIX make install 2>&1) +} + +ynh_use_nodejs () { + nodejs_version=$(ynh_app_setting_get $app nodejs_version) + + load_n_path="[[ :$PATH: == *\":$n_install_dir/bin:\"* ]] || PATH=\"$n_install_dir/bin:$PATH\"; N_PREFIX="$n_install_dir"" + + nodejs_use_version="$n_install_dir/bin/n -q $nodejs_version" + + # "Load" a version of node + eval $load_n_path; $nodejs_use_version + + # Get the absolute path of this version of node + nodejs_path="$(n bin $nodejs_version)" + + # Make an alias for node use + ynh_node_exec="eval $load_n_path; n use $nodejs_version" +} + +ynh_install_nodejs () { + # Use n, https://github.com/tj/n to manage the nodejs versions + nodejs_version="6" + local n_install_script="https://git.io/n-install" + + # Create $n_install_dir + mkdir -p "$n_install_dir" + + # Load n path in PATH + CLEAR_PATH="$n_install_dir/bin:$PATH" + # Remove /usr/local/bin in PATH in case of node has already setup. + PATH=$(echo $CLEAR_PATH | sed 's@/usr/local/bin:@@') + + # Move an existing node binary, to avoid to block n. + test -x /usr/bin/node && mv /usr/bin/node /usr/bin/node_n + test -x /usr/bin/npm && mv /usr/bin/npm /usr/bin/npm_n + + # If n is not previously setup, install it + if ! test n --version > /dev/null 2>&1 + then + ynh_install_n + fi + + # Modify the default N_PREFIX in n script + ynh_replace_string "^N_PREFIX=\${N_PREFIX-.*}$" "N_PREFIX=\${N_PREFIX-$N_PREFIX}" "$n_install_dir/bin/n" + + # Restore /usr/local/bin in PATH + PATH=$CLEAR_PATH + + # And replace the old node binary. + test -x /usr/bin/node_n && mv /usr/bin/node_n /usr/bin/node + test -x /usr/bin/npm_n && mv /usr/bin/npm_n /usr/bin/npm + + # Install the requested version of nodejs + n $nodejs_version + + # Find the last "real" version for this major version of node. + real_nodejs_version=$(find $node_version_path/$nodejs_version* -maxdepth 0 | sort --version-sort | tail --lines=1) + real_nodejs_version=$(basename $real_nodejs_version) + + # Create a symbolic link for this major version. If the file doesn't already exist + if [ ! -e "$node_version_path/$nodejs_version" ] + then + ln --symbolic --force --no-target-directory $node_version_path/$real_nodejs_version $node_version_path/$nodejs_version + fi + + # Store the ID of this app and the version of node requested for it + echo "$YNH_APP_ID:$nodejs_version" | tee --append "$n_install_dir/ynh_app_version" + + # Store nodejs_version into the config of this app + ynh_app_setting_set $app nodejs_version $nodejs_version + + # Build the update script and set the cronjob + ynh_cron_upgrade_node + + ynh_use_nodejs +} + +ynh_remove_nodejs () { + ynh_use_nodejs + + # Remove the line for this app + sed --in-place "/$YNH_APP_ID:$nodejs_version/d" "$n_install_dir/ynh_app_version" + + # If none another app uses this version of nodejs, remove it. + if ! grep --quiet "$nodejs_version" "$n_install_dir/ynh_app_version" + then + n rm $nodejs_version + fi + + # If none another app uses n, remove n + if [ ! -s "$n_install_dir/ynh_app_version" ] + then + ynh_secure_remove "$n_install_dir" + ynh_secure_remove "/usr/local/n" + sed --in-place "/N_PREFIX/d" /root/.bashrc + fi +} + +ynh_cron_upgrade_node () { + # Build the update script + cat > "$n_install_dir/node_update.sh" << EOF +#!/bin/bash +version_path="$node_version_path" +n_install_dir="$n_install_dir" +# Log the date +date +# List all real installed version of node +all_real_version="\$(find \$version_path/* -maxdepth 0 -type d | sed "s@\$version_path/@@g")" +# Keep only the major version number of each line +all_real_version=\$(echo "\$all_real_version" | sed 's/\..*\$//') +# Remove double entries +all_real_version=\$(echo "\$all_real_version" | sort --unique) +# Read each major version +while read version +do + echo "Update of the version \$version" + sudo \$n_install_dir/bin/n \$version + # Find the last "real" version for this major version of node. + real_nodejs_version=\$(find \$version_path/\$version* -maxdepth 0 | sort --version-sort | tail --lines=1) + real_nodejs_version=\$(basename \$real_nodejs_version) + # Update the symbolic link for this version + sudo ln --symbolic --force --no-target-directory \$version_path/\$real_nodejs_version \$version_path/\$version +done <<< "\$(echo "\$all_real_version")" +EOF + + chmod +x "$n_install_dir/node_update.sh" + + # Build the cronjob + cat > "/etc/cron.daily/node_update" << EOF +#!/bin/bash +$n_install_dir/node_update.sh >> $n_install_dir/node_update.log +EOF + + chmod +x "/etc/cron.daily/node_update" +} diff --git a/scripts/_variables b/scripts/_variables new file mode 100644 index 0000000..16181df --- /dev/null +++ b/scripts/_variables @@ -0,0 +1 @@ +nodejs_version=8 diff --git a/scripts/install b/scripts/install index 376168c..0d4aa42 100644 --- a/scripts/install +++ b/scripts/install @@ -1,68 +1,176 @@ #!/bin/bash -set -e +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Retrieve arguments -domain=$1 -path=$2 -is_public=$3 -final_path=/var/www/thelounge +source _variables +source _common.sh +source /usr/share/yunohost/helpers -# Check domain/path availability -sudo yunohost app checkurl $domain$path -a thelounge -path=${path%/} +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= -# Install dependencies -sudo apt-get update -sudo apt-get install nodejs -y +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors -# Create user -sudo useradd -d $final_path thelounge\ - || echo "User already created" +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= -# Modify the random username -user=$(dd if=/dev/urandom bs=1 count=200 2> /dev/null | tr -c -d 'A-Za-z0-9' | sed -n 's/\(.\{24\}\).*/\1/p') -sed -i "s@USERTOCHANGE@$user@g" ../conf/user.json -sed -i "s@PATHTOCHANGE@$path/@g" ../conf/config.js +domain=$YNH_APP_ARG_DOMAIN +path_url=$YNH_APP_ARG_PATH +is_public=$YNH_APP_ARG_IS_PUBLIC -# HACK: Change the socket.io path in the sources -sed -i "s@PATHTOCHANGE@$path@g" ../sources/client/js/lounge.js +# This is a multi-instance app, meaning it can be installed several times independently +# The id of the app as stated in the manifest is available as $YNH_APP_ID +# The instance number is available as $YNH_APP_INSTANCE_NUMBER (equals "1", "2", ...) +# The app instance name is available as $YNH_APP_INSTANCE_NAME +# - the first time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample +# - the second time the app is installed, YNH_APP_INSTANCE_NAME = ynhexample__2 +# - ynhexample__{N} for the subsequent installations, with N=3,4, ... +# The app instance name is probably what you are interested the most, since this is +# guaranteed to be unique. This is a good unique identifier to define installation path, +# db names, ... +app=$YNH_APP_INSTANCE_NAME -# Copy files to the right place -sudo mkdir -p $final_path/.lounge/users -sudo cp ../conf/config.js $final_path/.lounge/ -sudo cp ../conf/user.json $final_path/.lounge/users/$user.json -sudo cp -a ../sources/* $final_path/ +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= -# Set permissions -sudo chown -hR thelounge $final_path +final_path=/var/www/$app +config_path=/home/yunohost.app/$app/ +test ! -e "$final_path" || ynh_die "This path already contains a folder" -# Install dependencies -sudo su - thelounge -c "cd $final_path && /usr/bin/npm install" +# Normalize the url path syntax +path_url=$(ynh_normalize_url_path $path_url) -# Modify Nginx configuration file and copy it to Nginx conf directory -if [[ "$path" == "" ]]; then - sed -i "s@LOCATIONTOCHANGE@/@g" ../conf/nginx.conf -else - sed -i "s@LOCATIONTOCHANGE@$path@g" ../conf/nginx.conf +# Check web path availability +ynh_webpath_available $domain $path_url +# Register (book) web path +ynh_webpath_register $app $domain $path_url + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= + +ynh_app_setting_set $app domain $domain +ynh_app_setting_set $app path $path_url +ynh_app_setting_set $app is_public $is_public + + +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# FIND AND OPEN A PORT +#================================================= + +# Find a free port +port=$(ynh_find_port 9009) +# Open this port +yunohost firewall allow --no-upnp TCP $port 2>&1 +ynh_app_setting_set $app port $port + +#================================================= +# INSTALL DEPENDENCIES +#================================================= + + +ynh_install_nodejs $nodejs_version + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= + +ynh_app_setting_set $app final_path $final_path +mkdir -p $final_path +mkdir -p $config_path +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source $final_path + +#================================================= +# NGINX CONFIGURATION +#================================================= + +# Create a dedicated nginx config +ynh_add_nginx_config + +#================================================= +# CREATE DEDICATED USER +#================================================= + +# Create a system user +ynh_system_user_create $app + +#================================================= +# SETUP SYSTEMD +#================================================= + +# Create a dedicated systemd config +ynh_add_systemd_config + +#================================================= +# INSTALL THE LOUNGE +#================================================= +npm -g install $final_path + + +#================================================= +# MODIFY A CONFIG FILE +#================================================= +cp -a ../conf/config.js $config_path + +#================================================= +# STORE THE CHECKSUM OF THE CONFIG FILE +#================================================= + +# Calculate and store the config file checksum into the app settings +ynh_store_file_checksum "$config_path/config.js" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +# Set permissions to app files +chown -R $app: $final_path +chown -R $app: $config_path + +#================================================= +# SETUP LOGROTATE +#================================================= + +# Use logrotate to manage application logfile(s) +ynh_use_logrotate + +#================================================= +# ADVERTISE SERVICE IN ADMIN PANEL +#================================================= + + + +#================================================= +# SETUP SSOWAT +#================================================= + +if [ $is_public -eq 0 ] +then # Remove the public access + ynh_app_setting_delete $app skipped_uris fi -sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/thelounge.conf - -# Copy systemd service -sudo cp ../conf/systemd.service /etc/systemd/system/thelounge.service - -# Add YunoHost service -sudo yunohost service add thelounge -l /var/log/syslog - # Make app public if necessary -sudo yunohost app setting thelounge is_public -v "$is_public" -if [ "$is_public" = "Yes" ]; +if [ $is_public -eq 1 ] then - sudo yunohost app setting thelounge unprotected_uris -v "/" + # unprotected_uris allows SSO credentials to be passed anyway. + ynh_app_setting_set $app unprotected_uris "/" fi -# Reload Nginx, thelounge and regenerate SSOwat conf -sudo service nginx reload -sudo systemctl enable thelounge -sudo systemctl start thelounge || sudo systemctl restart thelounge -sudo yunohost app ssowatconf +#================================================= +# RELOAD NGINX +#================================================= +systemctl start thelounge +sleep 20 +systemctl reload nginx diff --git a/scripts/remove b/scripts/remove index e8c3922..67a238d 100644 --- a/scripts/remove +++ b/scripts/remove @@ -1,16 +1,88 @@ #!/bin/bash -domain=$(sudo yunohost app setting thelounge domain) +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -sudo systemctl stop thelounge -sudo systemctl disable thelounge -sudo yunohost service remove thelounge +source _common.sh +source /usr/share/yunohost/helpers -sudo rm -rf /var/www/thelounge -sudo rm -f /etc/nginx/conf.d/$domain.d/thelounge.conf -sudo rm -f /etc/systemd/system/thelounge.service +#================================================= +# LOAD SETTINGS +#================================================= -sudo userdel thelounge +app=$YNH_APP_INSTANCE_NAME -sudo service nginx reload -sudo yunohost app ssowatconf +domain=$(ynh_app_setting_get $app domain) +port=$(ynh_app_setting_get $app port) +final_path=$(ynh_app_setting_get $app final_path) + +#================================================= +# STANDARD REMOVE +#================================================= +# STOP AND REMOVE SERVICE +#================================================= + +# Remove the dedicated systemd config +ynh_remove_systemd_config + +#================================================= +# REMOVE SERVICE FROM ADMIN PANEL +#================================================= + +if yunohost service status | grep -q $app +then + echo "Remove $app service" + yunohost service remove $app +fi + +#================================================= +# REMOVE APP MAIN DIR +#================================================= + +# Remove the app directory securely +ynh_secure_remove "$final_path" + +#================================================= +# REMOVE NGINX CONFIGURATION +#================================================= + +# Remove the dedicated nginx config +ynh_remove_nginx_config + + +#================================================= +# REMOVE LOGROTATE CONFIGURATION +#================================================= + +# Remove the app-specific logrotate config +ynh_remove_logrotate + +#================================================= +# CLOSE A PORT +#================================================= + +if yunohost firewall list | grep -q "\- $port$" +then + echo "Close port $port" + yunohost firewall disallow TCP $port 2>&1 +fi + +#================================================= +# SPECIFIC REMOVE +#================================================= + +ynh_use_nodejs +npm uninstall $app +ynh_remove_nodejs + +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= + +# Delete a system user +ynh_system_user_delete $app diff --git a/scripts/upgrade b/scripts/upgrade index a127a2b..9fced40 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,55 +1,154 @@ #!/bin/bash -set -e +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= -# Retrieve arguments -domain=$(sudo yunohost app setting thelounge domain) -path=$(sudo yunohost app setting thelounge path) -is_public=$(sudo yunohost app setting thelounge is_public) -final_path=/var/www/thelounge +source _common.sh +source /usr/share/yunohost/helpers +source _variables -# Remove trailing "/" from the path -path=${path%/} +#================================================= +# LOAD SETTINGS +#================================================= -# HACK: Change the socket.io path in the sources -sed -i "s@PATHTOCHANGE@$path@g" ../sources/client/js/thelounge.js +app=$YNH_APP_INSTANCE_NAME -# Copy files to the right place -sudo mkdir -p $final_path/.thelounge/users -sed -i "s@PATHTOCHANGE@$path/@g" ../conf/config.js -sudo cp ../conf/config.js $final_path/.thelounge/ -sudo cp -a ../sources/* $final_path/ +domain=$(ynh_app_setting_get $app domain) +path_url=$(ynh_app_setting_get $app path) +is_public=$(ynh_app_setting_get $app is_public) +final_path=$(ynh_app_setting_get $app final_path) +port=$(ynh_app_setting_get $app port) -# Set permissions -sudo chown -hR thelounge $final_path +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= -# Install dependencies -sudo su -c "cd $final_path && /usr/bin/npm install --production" thelounge - -# Modify Nginx configuration file and copy it to Nginx conf directory -if [[ "$path" == "" ]]; then - sed -i "s@LOCATIONTOCHANGE@/@g" ../conf/nginx.conf -else - sed -i "s@LOCATIONTOCHANGE@$path@g" ../conf/nginx.conf +# Fix is_public as a boolean value +if [ "$is_public" = "Yes" ]; then + ynh_app_setting_set $app is_public 1 + is_public=1 +elif [ "$is_public" = "No" ]; then + ynh_app_setting_set $app is_public 0 + is_public=0 fi -sudo cp ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/thelounge.conf -# Copy systemd service -sudo cp ../conf/systemd.service /etc/systemd/system/thelounge.service +# If final_path doesn't exist, create it +if [ -z $final_path ]; then + final_path=/var/www/$app + ynh_app_setting_set $app final_path $final_path +fi -# Add YunoHost service -sudo yunohost service add thelounge -l /var/log/syslog \ - || echo "Service already exist" +# If config_path doesn't exist, create it +if [ -z $config_path ]; then + config_path=/home/yunohost.app/$app + ynh_app_setting_set $app config_path $config_path +fi +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= + +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# CHECK THE PATH +#================================================= + +# Normalize the URL path syntax +path_url=$(ynh_normalize_url_path $path_url) + +#================================================= +# STANDARD UPGRADE STEPS +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= + +# Upgrade Nodejs +ynh_install_nodejs + +# Download, check integrity, uncompress and patch the source from app.src +ynh_setup_source $final_path +ynh_use_nodejs +npm -g upgrade thelounge $final_path + + +#================================================= +# NGINX CONFIGURATION +#================================================= + +# Create a dedicated nginx config +ynh_add_nginx_config + +#================================================= +# CREATE DEDICATED USER +#================================================= + +# Create a system user +ynh_system_user_create $app + +#================================================= +# SPECIFIC UPGRADE +#================================================= +# ... +#================================================= + +# Verify the checksum and backup the file if it's different +ynh_backup_if_checksum_is_different "$final_path/CONFIG_FILE" +# Recalculate and store the config file checksum into the app settings +ynh_store_file_checksum "$config_path/config.js" + +#================================================= +# SETUP LOGROTATE +#================================================= + +# Use logrotate to manage app-specific logfile(s) +ynh_use_logrotate --non-append + +#================================================= +# SETUP SYSTEMD +#================================================= + +# Create a dedicated systemd config +ynh_add_systemd_config + +#================================================= +# GENERIC FINALIZATION +#================================================= +# SECURE FILES AND DIRECTORIES +#================================================= + +# Set right permissions for curl installation +chown -R $app: $final_path +chown -R $app: $config_path + +#================================================= +# SETUP SSOWAT +#================================================= + +if [ $is_public -eq 0 ] +then # Remove the public access + ynh_app_setting_delete $app skipped_uris +fi # Make app public if necessary -sudo yunohost app setting thelounge is_public -v "$is_public" -if [ "$is_public" = "Yes" ]; +if [ $is_public -eq 1 ] then - sudo yunohost app setting thelounge unprotected_uris -v "/" + # unprotected_uris allows SSO credentials to be passed anyway + ynh_app_setting_set $app unprotected_uris "/" fi -# Reload Nginx, thelounge and regenerate SSOwat conf -sudo service nginx reload -sudo systemctl enable thelounge -sudo systemctl restart thelounge || sudo systemctl start thelounge -sudo yunohost app ssowatconf +#================================================= +# RELOAD NGINX +#================================================= + +systemctl restart thelounge +systemctl reload nginx diff --git a/sources/.travis.yml b/sources/.travis.yml deleted file mode 100644 index 6e5919d..0000000 --- a/sources/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - "0.10" diff --git a/sources/CHANGELOG.md b/sources/CHANGELOG.md deleted file mode 100644 index 775cec4..0000000 --- a/sources/CHANGELOG.md +++ /dev/null @@ -1,808 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -This project adheres to [Semantic Versioning](http://semver.org/). - - - -## v2.1.0 - 2016-10-17 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.1...v2.1.0) - -Here comes another release with some nice additions! - -While the administrators will notice some bug fixes, most of the changes are client-side: support for `/list`, a slideout menu on mobile, editing one's nick from the UI, wallops message handling. - -Enjoy! - -### Added - -- Implement `/list` ([#258](https://github.com/thelounge/lounge/pull/258) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Add touch slideout menu for mobile ([#400](https://github.com/thelounge/lounge/pull/400) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Display extra steps when loading the app ([#637](https://github.com/thelounge/lounge/pull/637) by [@xPaw](https://github.com/xPaw)) -- Display localized timestamp in title of message times ([#660](https://github.com/thelounge/lounge/pull/660) by [@astorije](https://github.com/astorije)) -- Changing nick in the UI ([#551](https://github.com/thelounge/lounge/pull/551) by [@astorije](https://github.com/astorije)) -- Add hostmasks in logs when possible ([#670](https://github.com/thelounge/lounge/pull/670) by [@astorije](https://github.com/astorije)) -- Display wallops in server window ([#658](https://github.com/thelounge/lounge/pull/658) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Make use of multi-prefix cap and remove NAMES spam on mode changes ([#632](https://github.com/thelounge/lounge/pull/632) by [@xPaw](https://github.com/xPaw)) -- Strict mode for all JS files ([#684](https://github.com/thelounge/lounge/pull/684) by [@astorije](https://github.com/astorije)) -- Enforce more ESLint rules ([#681](https://github.com/thelounge/lounge/pull/681) by [@xPaw](https://github.com/xPaw)) -- Use CI caches for downloaded files instead of installed ones ([#687](https://github.com/thelounge/lounge/pull/687) by [@astorije](https://github.com/astorije)) -- Consolidate version numbers throughout all interfaces ([#592](https://github.com/thelounge/lounge/pull/592) by [@williamboman](https://github.com/williamboman)) -- Replace lodash's each/map with ES5 native forEach/map ([#689](https://github.com/thelounge/lounge/pull/689) by [@astorije](https://github.com/astorije)) - -### Removed - -- Remove all font files except WOFF ([#682](https://github.com/thelounge/lounge/pull/682) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Themes: Fixed CSS rule selectors for highlight messages ([#652](https://github.com/thelounge/lounge/pull/652) by [@DamonGant](https://github.com/DamonGant)) -- Fix unhandled message color in default and Crypto themes ([#653](https://github.com/thelounge/lounge/pull/653) by [@MaxLeiter](https://github.com/MaxLeiter)) -- Check if SSL key and certificate files exist ([#673](https://github.com/thelounge/lounge/pull/673) by [@toXel](https://github.com/toXel)) -- Fix loading fonts in Microsoft Edge ([#683](https://github.com/thelounge/lounge/pull/683) by [@xPaw](https://github.com/xPaw)) -- Fill in prefixLookup on network initialization ([#647](https://github.com/thelounge/lounge/pull/647) by [@nornagon](https://github.com/nornagon)) -- Fix nick changes not being properly reported in the logs ([#685](https://github.com/thelounge/lounge/pull/685) by [@astorije](https://github.com/astorije)) -- Fix memory and reference shuffling when creating models ([#664](https://github.com/thelounge/lounge/pull/664) by [@xPaw](https://github.com/xPaw)) - -## v2.0.1 - 2016-09-28 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0...v2.0.1) - -This is a minor house-keeping release with mostly two sets of changes. - -First, a few bugs were fixed, including one simply preventing The Lounge to run in Safari's private browsing. - -Additionally, the developer experience has been made a tiny bit better, with better documentation, lighter dependencies and simpler theme creation. - -### Changed - -- Add info on README about how to run from source, how to upgrade ([#621](https://github.com/thelounge/lounge/pull/621) by [@astorije](https://github.com/astorije)) -- Move uglify invocation into npm scripts and remove grunt ([#628](https://github.com/thelounge/lounge/pull/628) by [@nornagon](https://github.com/nornagon)) -- Move Shout theme borders to example theme ([#359](https://github.com/thelounge/lounge/pull/359) by [@xPaw](https://github.com/xPaw)) -- Update developer dependencies ([#639](https://github.com/thelounge/lounge/pull/639) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Remove -ms-transform and add missed -webkit-transform ([#629](https://github.com/thelounge/lounge/pull/629) by [@xPaw](https://github.com/xPaw)) -- Ensure localStorage cannot fail because of quota or Safari private browsing ([#625](https://github.com/thelounge/lounge/pull/625) by [@astorije](https://github.com/astorije)) -- Disable pull-to-refresh on mobile that conflicts with scrolling the message list ([#618](https://github.com/thelounge/lounge/pull/618) by [@astorije](https://github.com/astorije)) -- Handle stderr when using edit or config command ([#622](https://github.com/thelounge/lounge/pull/622) by [@MaxLeiter](https://github.com/MaxLeiter)) - -## v2.0.0 - 2016-09-24 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.5.0...v2.0.0) - -After more than 5 months in the works, v2.0.0 is finally happening, and it's shipping with lots of new and enhanced features! 🎉 - -First of all, the backend IRC library is completely different, which was the first step to deciding on a major release. -This change brings many improvements and fixes, including support for auto-reconnection! This also allows us to easily improve our [IRCv3 compliance](http://ircv3.net/software/clients.html#web-clients). - -Main changes on the server include support for WEBIRC, oidentd and LDAP. On the client, users will notice a lot of improvements about reporting unseen activity (notifications, markers, etc.), support for custom highlights, a new loading page, an auto-expanding message input, a theme selector, and more. - -Administrators should note that the channel list format in user configuration files has changed. The old format is deprecated, but it will be automatically converted when the server starts (support may or may not be removed later). Additionally, The Lounge now only runs on Node v4 and up. - -The above is only a small subset of changes. A more detailed list can be found below. -The following list features the most noticeable changes only, and more details can be found on all [v2.0.0 pre-releases](https://www.github.com/thelounge/lounge/releases). - -### Added - -- Add tooltips on every clickable icons ([#540](https://github.com/thelounge/lounge/pull/540) by [@astorije](https://github.com/astorije)) -- Add debug config option for `irc-framework` debug log ([#547](https://github.com/thelounge/lounge/pull/547) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Client-side theme selector ([#568](https://github.com/thelounge/lounge/pull/568) by [@astorije](https://github.com/astorije)) -- LDAP support ([#477](https://github.com/thelounge/lounge/pull/477) by [@thisisdarshan](https://github.com/thisisdarshan) and [@lindskogen](https://github.com/lindskogen)) -- Add custom highlights ([#425](https://github.com/thelounge/lounge/pull/425) by [@YaManicKill](https://github.com/YaManicKill)) -- Add auto-grow textarea support ([#379](https://github.com/thelounge/lounge/pull/379) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Display unhandled numerics on the client ([#286](https://github.com/thelounge/lounge/pull/286) by [@xPaw](https://github.com/xPaw)) -- A proper unread marker ([#332](https://github.com/thelounge/lounge/pull/332) by [@xPaw](https://github.com/xPaw)) -- Add information on the About section of the client ([#497](https://github.com/thelounge/lounge/pull/497) by [@astorije](https://github.com/astorije)) -- Add a red dot to the mobile menu icon when being notified ([#486](https://github.com/thelounge/lounge/pull/486) by [@astorije](https://github.com/astorije)) -- Add "The Lounge" label to the landing pages ([#487](https://github.com/thelounge/lounge/pull/487) by [@astorije](https://github.com/astorije)) -- Display network name on Connect page when network is locked and info is hidden ([#488](https://github.com/thelounge/lounge/pull/488) by [@astorije](https://github.com/astorije)) -- Display a loading message instead of blank page ([#386](https://github.com/thelounge/lounge/pull/386) by [@xPaw](https://github.com/xPaw)) -- Fall back to LOUNGE_HOME env variable when using the CLI ([#402](https://github.com/thelounge/lounge/pull/402) by [@williamboman](https://github.com/williamboman)) -- Enable auto reconnection ([#254](https://github.com/thelounge/lounge/pull/254) by [@xPaw](https://github.com/xPaw)) -- Add "!" modechar for admin ([#354](https://github.com/thelounge/lounge/pull/354) by [@omnicons](https://github.com/omnicons)) -- Add support for oidentd spoofing ([#256](https://github.com/thelounge/lounge/pull/256) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Log enabled capabilities ([#272](https://github.com/thelounge/lounge/pull/272) by [@xPaw](https://github.com/xPaw)) -- Add support for `~` home folder expansion ([#284](https://github.com/thelounge/lounge/pull/284) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Document supported node version ([#280](https://github.com/thelounge/lounge/pull/280) by [@xPaw](https://github.com/xPaw)) -- Implement WEBIRC ([#240](https://github.com/thelounge/lounge/pull/240) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Add `manifest.json` for nicer mobile experience ([#310](https://github.com/thelounge/lounge/pull/310) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Cache loaded config and merge it with defaults ([#387](https://github.com/thelounge/lounge/pull/387) by [@xPaw](https://github.com/xPaw)) -- Ignore unnecessary files at release time ([#499](https://github.com/thelounge/lounge/pull/499) by [@astorije](https://github.com/astorije)) -- Improve font icon management, sizing and sharpness ([#493](https://github.com/thelounge/lounge/pull/493) by [@astorije](https://github.com/astorije)) -- Maintain scroll position after loading previous messages ([#496](https://github.com/thelounge/lounge/pull/496) by [@davibe](https://github.com/davibe)) -- Perform node version check as soon as possible ([#409](https://github.com/thelounge/lounge/pull/409) by [@xPaw](https://github.com/xPaw)) -- Prepend http protocol to www. links in chat ([#410](https://github.com/thelounge/lounge/pull/410) by [@xPaw](https://github.com/xPaw)) -- Change default configuration for `host` to allow OS to decide and use both IPv4 and IPv6 ([#432](https://github.com/thelounge/lounge/pull/432) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Do not hide timestamps on small viewports ([#376](https://github.com/thelounge/lounge/pull/376) by [@xPaw](https://github.com/xPaw)) -- Drop `slate-irc`, switch to `irc-framework` ([#167](https://github.com/thelounge/lounge/pull/167) by [@xPaw](https://github.com/xPaw)) -- Improve sticky scroll ([#262](https://github.com/thelounge/lounge/pull/262) by [@xPaw](https://github.com/xPaw)) -- Minor wording changes for better clarity ([#305](https://github.com/thelounge/lounge/pull/305) by [@astorije](https://github.com/astorije)) -- Improve nick highlights ([#327](https://github.com/thelounge/lounge/pull/327) by [@xPaw](https://github.com/xPaw)) -- CSS classes in themes for nick colors ([#325](https://github.com/thelounge/lounge/pull/325) by [@astorije](https://github.com/astorije)) -- Replace all concatenated paths with Node's path.join ([#307](https://github.com/thelounge/lounge/pull/307) by [@astorije](https://github.com/astorije)) - -### Deprecated - -- Store channels in array format in user configuration files, deprecating previous format ([#417](https://github.com/thelounge/lounge/pull/417) by [@xPaw](https://github.com/xPaw)) - -### Removed - -- Disable tooltips on mobile to prevent them to stay after clicking ([#612](https://github.com/thelounge/lounge/pull/612) by [@astorije](https://github.com/astorije)) -- Remove Docker-related files now that we have a dedicated repository ([#288](https://github.com/thelounge/lounge/pull/288) by [@astorije](https://github.com/astorije)) -- Remove JavaScript scrollbar library ([#429](https://github.com/thelounge/lounge/pull/429) by [@xPaw](https://github.com/xPaw)) -- Remove navigator.standalone detection ([#427](https://github.com/thelounge/lounge/pull/427) by [@xPaw](https://github.com/xPaw)) -- Do not increase font size on highlight in morning theme ([#321](https://github.com/thelounge/lounge/pull/321) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Remove font family redundancy, fix missed fonts, remove Open Sans ([#562](https://github.com/thelounge/lounge/pull/562) by [@astorije](https://github.com/astorije)) -- Stop propagation when hiding the chat through click/tapping the chat ([#455](https://github.com/thelounge/lounge/pull/455) by [@williamboman](https://github.com/williamboman)) -- Improve click handling on users and inline channels ([#366](https://github.com/thelounge/lounge/pull/366) by [@xPaw](https://github.com/xPaw)) -- Only load config if it exists ([#461](https://github.com/thelounge/lounge/pull/461) by [@xPaw](https://github.com/xPaw)) -- Send user to lobby of deleted chan when parting from active chan ([#489](https://github.com/thelounge/lounge/pull/489) by [@astorije](https://github.com/astorije)) -- Set title attribute on topic on initial page load ([#515](https://github.com/thelounge/lounge/pull/515) by [@williamboman](https://github.com/williamboman)) -- Save user's channels when they sort the channel list ([#401](https://github.com/thelounge/lounge/pull/401) by [@xPaw](https://github.com/xPaw)) -- Turn favicon red on page load if there are highlights ([#344](https://github.com/thelounge/lounge/pull/344) by [@xPaw](https://github.com/xPaw)) -- Keep chat stickied to the bottom on resize ([#346](https://github.com/thelounge/lounge/pull/346) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Only increase unread counter for whitelisted actions ([#273](https://github.com/thelounge/lounge/pull/273) by [@xPaw](https://github.com/xPaw)) -- Parse CTCP replies ([#278](https://github.com/thelounge/lounge/pull/278) by [@xPaw](https://github.com/xPaw)) -- Do not count your own messages as unread ([#279](https://github.com/thelounge/lounge/pull/279) by [@xPaw](https://github.com/xPaw)) -- Do not display incorrect nick when switching to a non connected network ([#252](https://github.com/thelounge/lounge/pull/252) by [@xPaw](https://github.com/xPaw)) -- Keep autocompletion sort whenever user list updates ([#217](https://github.com/thelounge/lounge/pull/217) by [@xPaw](https://github.com/xPaw)) -- Save user when parting channels ([#297](https://github.com/thelounge/lounge/pull/297) by [@xPaw](https://github.com/xPaw)) -- Add labels in connect window ([#300](https://github.com/thelounge/lounge/pull/300) by [@xPaw](https://github.com/xPaw)) -- Add missing `aria-label` on icon buttons ([#303](https://github.com/thelounge/lounge/pull/303) by [@astorije](https://github.com/astorije)) -- Fix missing colors in action messages ([#317](https://github.com/thelounge/lounge/pull/317) by [@astorije](https://github.com/astorije)) -- Don't falsely report failed write if it didn't fail ([`e6990e0`](https://github.com/thelounge/lounge/commit/e6990e0fc7641d18a5bcbabddca1aacf2254ae52) by [@xPaw](https://github.com/xPaw)) -- Fix sending messages starting with a space ([#320](https://github.com/thelounge/lounge/pull/320) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix notifications in query windows ([#334](https://github.com/thelounge/lounge/pull/334) by [@xPaw](https://github.com/xPaw)) - -### Security - -- Implement user token persistency ([#370](https://github.com/thelounge/lounge/pull/370) by [@xPaw](https://github.com/xPaw)) -- Restrict access to the home directory by default ([#205](https://github.com/thelounge/lounge/pull/205) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Add security headers to minimize XSS damage ([#292](https://github.com/thelounge/lounge/pull/292) by [@xPaw](https://github.com/xPaw)) -- Do not write user configs outside of the app's users directory ([#238](https://github.com/thelounge/lounge/pull/238) by [@williamboman](https://github.com/williamboman)) -- Don't check for existing password emptiness ([#315](https://github.com/thelounge/lounge/pull/315) by [@maxpoulin64](https://github.com/maxpoulin64)) - -## v2.0.0-rc.2 - 2016-09-21 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-rc.1...v2.0.0-rc.2) - -This release candidate only fixes a UI bug affecting iOS 8 users, introduced in v2.0.0-pre.5. - -### Fixed - -- Fix flexboxes to work on iOS 8 ([#626](https://github.com/thelounge/lounge/pull/626) by [@Gilles123](https://github.com/Gilles123)) - -## v2.0.0-rc.1 - 2016-09-17 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.7...v2.0.0-rc.1) - -Prior to this release, users of Safari 10 were not able to access The Lounge anymore, because of a conscious change the WebKit made to their support of CSP, as [explained here](https://webkit.org/blog/6830/a-refined-content-security-policy/). This release addresses this issue. - -Another notable change is the removal of tooltips on mobiles, as hovering states on mobile devices breaks in different kind of ways. Hopefully there will be a better solution in the future, or better support across mobiles. - -This is also the first release candidate for v2.0.0. This means only critical bug fixes will be merged before releasing v2.0.0. - -### Changed - -- Explicitly authorize websockets in CSP header ([#597](https://github.com/thelounge/lounge/pull/597) by [@astorije](https://github.com/astorije)) - -### Removed - -- Disable tooltips on mobile to prevent them to stay after clicking ([#612](https://github.com/thelounge/lounge/pull/612) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Fix small input text on Morning and Zenburn ([#601](https://github.com/thelounge/lounge/pull/601) by [@astorije](https://github.com/astorije)) -- Fix a left margin appearing on all non-default themes ([#615](https://github.com/thelounge/lounge/pull/615) by [@astorije](https://github.com/astorije)) - -## v2.0.0-pre.7 - 2016-09-08 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.6...v2.0.0-pre.7) - -This prerelease fixes a lot of bugs on both the server and the client. It also adds a theme selector on the client and connection debug log level on the server. Additionally, custom highlights are now case-insensitive. - -### Added - -- Add tooltips on every clickable icons ([#540](https://github.com/thelounge/lounge/pull/540) by [@astorije](https://github.com/astorije)) -- Add debug config option for `irc-framework` debug log ([#547](https://github.com/thelounge/lounge/pull/547) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Client-side theme selector ([#568](https://github.com/thelounge/lounge/pull/568) by [@astorije](https://github.com/astorije)) - -### Changed - -- Use our logger instead of `console.log` and `console.error` for LDAP logs ([#552](https://github.com/thelounge/lounge/pull/552) by [@astorije](https://github.com/astorije)) -- Make custom highlights case-insensitive ([#565](https://github.com/thelounge/lounge/pull/565) by [@astorije](https://github.com/astorije)) -- Bump `request` dependency to 2.74.0 ([#563](https://github.com/thelounge/lounge/pull/563) by [@astorije](https://github.com/astorije)) -- Mention wiki in README ([#548](https://github.com/thelounge/lounge/pull/548) by [@MaxLeiter](https://github.com/MaxLeiter)) -- Support ES6 features in JS linting outside of client code ([#593](https://github.com/thelounge/lounge/pull/593) by [@williamboman](https://github.com/williamboman)) - -### Fixed - -- Fix token persistency across server refreshes ([#553](https://github.com/thelounge/lounge/pull/553) by [@astorije](https://github.com/astorije)) -- Make sure input height is reset when submitting with icon ([#555](https://github.com/thelounge/lounge/pull/555) by [@astorije](https://github.com/astorije)) -- Fix webirc and 4-in-6 addresses ([#535](https://github.com/thelounge/lounge/pull/535) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Allow long URLs to break onto next line on Chrome ([#576](https://github.com/thelounge/lounge/pull/576) by [@astorije](https://github.com/astorije)) -- Make sure users with wrong tokens are locked out instead of crashing the app ([#570](https://github.com/thelounge/lounge/pull/570) by [@astorije](https://github.com/astorije)) -- Remove font family redundancy, fix missed fonts, remove Open Sans ([#562](https://github.com/thelounge/lounge/pull/562) by [@astorije](https://github.com/astorije)) -- Do not set app orientation in manifest to use user setting at OS level ([#587](https://github.com/thelounge/lounge/pull/587) by [@astorije](https://github.com/astorije)) -- Move border-radius from `#main` to `.window elements` to fix radius once and for all ([#572](https://github.com/thelounge/lounge/pull/572) by [@astorije](https://github.com/astorije)) - -## v2.0.0-pre.6 - 2016-08-10 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.5...v2.0.0-pre.6) - -LDAP! That's all there is to be found in this pre-release, but it should please some administrators out there. Big thanks to [@thisisdarshan](https://github.com/thisisdarshan) and [@lindskogen](https://github.com/lindskogen) for sticking with us on this one. - -This feature will remain in beta version until the official v2.0.0 release. - -### Added - -- LDAP support ([#477](https://github.com/thelounge/lounge/pull/477) by [@thisisdarshan](https://github.com/thisisdarshan) and [@lindskogen](https://github.com/lindskogen)) - -## v2.0.0-pre.5 - 2016-08-07 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.4...v2.0.0-pre.5) - -What an exciting release! It's been in the works for more than a month, but the perks are worth the wait. - -On the user side, some long-awaited new features can now be found: The Lounge can now track custom highlights, it comes with an auto-expanding text field, and an unread message marker helps keeping track of what happened when you were not watching. A lot of improvements and various bug fixes have been made to the UI. -Note that scrollbar look-and-feel is now delegated to the browser and OS. Use the custom CSS editor and your OS settings to customize them. - -Administrators will notice a different format for channels in the user configuration files, and the Docker-related files have been moved to [a dedicated repository](https://github.com/thelounge/docker-lounge). Many bugs have been solved on the server as well. - -### Added - -- Add custom highlights ([#425](https://github.com/thelounge/lounge/pull/425) by [@YaManicKill](https://github.com/YaManicKill)) -- Add auto-grow textarea support ([#379](https://github.com/thelounge/lounge/pull/379) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Display unhandled numerics on the client ([#286](https://github.com/thelounge/lounge/pull/286) by [@xPaw](https://github.com/xPaw)) -- A proper unread marker ([#332](https://github.com/thelounge/lounge/pull/332) by [@xPaw](https://github.com/xPaw)) -- Add information on the About section of the client ([#497](https://github.com/thelounge/lounge/pull/497) by [@astorije](https://github.com/astorije)) -- Add a red dot to the mobile menu icon when being notified ([#486](https://github.com/thelounge/lounge/pull/486) by [@astorije](https://github.com/astorije)) -- Add "The Lounge" label to the landing pages ([#487](https://github.com/thelounge/lounge/pull/487) by [@astorije](https://github.com/astorije)) -- Display network name on Connect page when network is locked and info is hidden ([#488](https://github.com/thelounge/lounge/pull/488) by [@astorije](https://github.com/astorije)) - -### Changed - -- Store channels in array format in user configuration files ([#417](https://github.com/thelounge/lounge/pull/417) by [@xPaw](https://github.com/xPaw)) -- Cache loaded config and merge it with defaults ([#387](https://github.com/thelounge/lounge/pull/387) by [@xPaw](https://github.com/xPaw)) -- Ignore unnecessary files at release time ([#499](https://github.com/thelounge/lounge/pull/499) by [@astorije](https://github.com/astorije)) -- Improve font icon management, sizing and sharpness ([#493](https://github.com/thelounge/lounge/pull/493) by [@astorije](https://github.com/astorije)) -- Maintain scroll position after loading previous messages ([#496](https://github.com/thelounge/lounge/pull/496) by [@davibe](https://github.com/davibe)) - -### Removed - -- Remove Docker-related files ([#288](https://github.com/thelounge/lounge/pull/288) by [@astorije](https://github.com/astorije)) -- Remove JavaScript scrollbar library ([#429](https://github.com/thelounge/lounge/pull/429) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Fix storing the updated authentication token ([#437](https://github.com/thelounge/lounge/pull/437) by [@williamboman](https://github.com/williamboman)) -- Update `irc-framework` to 2.3.0 to fix a bug occurring when posting messages starting with a colon ([#449](https://github.com/thelounge/lounge/pull/449) by [@xPaw](https://github.com/xPaw)) -- Update `irc-framework` to 2.4.0 to fix a buffering issue ([#451](https://github.com/thelounge/lounge/pull/451) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Only auto join actual channels ([#453](https://github.com/thelounge/lounge/pull/453) by [@xPaw](https://github.com/xPaw)) -- Only trigger custom highlights for non-self messages and notices ([#454](https://github.com/thelounge/lounge/pull/454) by [@xPaw](https://github.com/xPaw)) -- Stop propagation when hiding the chat through click/tapping the chat ([#455](https://github.com/thelounge/lounge/pull/455) by [@williamboman](https://github.com/williamboman)) -- Improve click handling on users and inline channels ([#366](https://github.com/thelounge/lounge/pull/366) by [@xPaw](https://github.com/xPaw)) -- Update `irc-framework` to 2.5.0 to fix reconnection counter not being reset ([#451](https://github.com/thelounge/lounge/pull/451) by [@xPaw](https://github.com/xPaw)) -- Register irc-framework events before connecting ([#458](https://github.com/thelounge/lounge/pull/458) by [@xPaw](https://github.com/xPaw)) -- Only load config if it exists ([#461](https://github.com/thelounge/lounge/pull/461) by [@xPaw](https://github.com/xPaw)) -- Fix window layout a bit ([#465](https://github.com/thelounge/lounge/pull/465) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix slight bugs introduced by #379 and #465 ([#467](https://github.com/thelounge/lounge/pull/467) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Prevent the app from crashing when no theme is specified ([#474](https://github.com/thelounge/lounge/pull/474) by [@astorije](https://github.com/astorije)) -- Fix unread marker disappearing when opacity set to 1 ([#471](https://github.com/thelounge/lounge/pull/471) by [@astorije](https://github.com/astorije)) -- Fix breaking layout when switching portrait/landscape modes ([#478](https://github.com/thelounge/lounge/pull/478) by [@astorije](https://github.com/astorije)) -- Fix chat not being "stickied" to the bottom when joining channel ([#484](https://github.com/thelounge/lounge/pull/484) by [@williamboman](https://github.com/williamboman)) -- Add self info to TOGGLE messages to prevent unread marker to render for oneself ([#473](https://github.com/thelounge/lounge/pull/473) by [@astorije](https://github.com/astorije)) -- Send user to lobby of deleted chan when parting from active chan ([#489](https://github.com/thelounge/lounge/pull/489) by [@astorije](https://github.com/astorije)) -- Use `min-height` of textarea when computing auto-resize after deleting a char ([#504](https://github.com/thelounge/lounge/pull/504) by [@astorije](https://github.com/astorije)) -- Set title attribute on topic on initial page load ([#515](https://github.com/thelounge/lounge/pull/515) by [@williamboman](https://github.com/williamboman)) -- Make sure git commit check for the About section would not send stderr to the console ([#516](https://github.com/thelounge/lounge/pull/516) by [@astorije](https://github.com/astorije)) -- Create a single function to render networks to reduce code duplication ([#445](https://github.com/thelounge/lounge/pull/445) by [@xPaw](https://github.com/xPaw)) -- Reset the unread marker on channel change ([#527](https://github.com/thelounge/lounge/pull/527) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix accidentally removed border-radius ([#537](https://github.com/thelounge/lounge/pull/537) by [@astorije](https://github.com/astorije)) -- Fix font size in themes for new textarea ([#536](https://github.com/thelounge/lounge/pull/536) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Restore padding and height of message input pre-textarea era ([#539](https://github.com/thelounge/lounge/pull/539) by [@astorije](https://github.com/astorije)) -- Prevent Ctrl-Tab from triggering tab completion ([#541](https://github.com/thelounge/lounge/pull/541) by [@hho](https://github.com/hho)) - -## v2.0.0-pre.4 - 2016-06-29 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.3...v2.0.0-pre.4) - -This pre-release adds a loading window, helpful on slow connections. -It also implements token persistency, ensuring users do not have to authenticate at every server restart. As a side effect, security is improved by forcing logging out users on all devices when changing their password. - -All generated URLs are now HTTP by default, except when explicitly set to HTTPS. For example, `www.example.com` will link to `http://www.example.com`. One needs to share `https://www.example.com` to point others to a HTTPS location. - -As a few users have been having issues when running The Lounge with a non-supported Node.js version, we now detect it early to avoid cryptic errors. - -This pre-release also adds minor UI improvements, and fixes from the previous version. -While The Lounge still needs a lot of efforts to be fully accessible, this version slightly improves accessibility on clickable nickname. - -Internally, we now keep track of our code coverage, which we do not enforce strictly at the moment. - -### Added - -- Add code coverage ([#408](https://github.com/thelounge/lounge/pull/408) by [@astorije](https://github.com/astorije)) -- Display a loading message instead of blank page ([#386](https://github.com/thelounge/lounge/pull/386) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Perform node version check as soon as possible ([#409](https://github.com/thelounge/lounge/pull/409) by [@xPaw](https://github.com/xPaw)) -- Prepend http protocol to www. links in chat ([#410](https://github.com/thelounge/lounge/pull/410) by [@xPaw](https://github.com/xPaw)) -- Use tabs when saving user configs ([#418](https://github.com/thelounge/lounge/pull/418) by [@xPaw](https://github.com/xPaw)) -- Do not display the sidebar on sign-in page ([#420](https://github.com/thelounge/lounge/pull/420) by [@astorije](https://github.com/astorije)) -- Make style of loading page similar to other pages ([#423](https://github.com/thelounge/lounge/pull/423) by [@astorije](https://github.com/astorije)) -- Change default configuration for `host` to allow OS to decide and use both IPv4 and IPv6 ([#432](https://github.com/thelounge/lounge/pull/432) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Change nicks from links to spans everywhere ([#428](https://github.com/thelounge/lounge/pull/428) by [@xPaw](https://github.com/xPaw)) -- Increase join delay at connection to 1000ms ([#434](https://github.com/thelounge/lounge/pull/434) by [@williamboman](https://github.com/williamboman)) - -### Removed - -- Remove navigator.standalone detection ([#427](https://github.com/thelounge/lounge/pull/427) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Do not lose authentication token when the connection gets lost ([#369](https://github.com/thelounge/lounge/pull/369) by [@xPaw](https://github.com/xPaw)) -- Fix crash in public mode ([#413](https://github.com/thelounge/lounge/pull/413) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Do not print user loaded message in public mode ([#415](https://github.com/thelounge/lounge/pull/415) by [@xPaw](https://github.com/xPaw)) -- Fix focusing input when clicking chat container on the client ([#364](https://github.com/thelounge/lounge/pull/364) by [@williamboman](https://github.com/williamboman)) -- Fix channel join regression and fix possibly joining parted channels ([#411](https://github.com/thelounge/lounge/pull/411) by [@xPaw](https://github.com/xPaw)) - -### Security - -- Implement user token persistency ([#370](https://github.com/thelounge/lounge/pull/370) by [@xPaw](https://github.com/xPaw)) - -## v2.0.0-pre.3 - 2016-06-15 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.2...v2.0.0-pre.3) - -This release introduces a few internal changes as well as two noticeable ones. When using the CLI, the home path can now be set with the `LOUNGE_HOME` environment variable, to avoid repeating `--home` over and over. On the client, sorting channels will now be saved in the user configuration. - -### Added - -- Fall back to LOUNGE_HOME env variable when using the CLI ([#402](https://github.com/thelounge/lounge/pull/402) by [@williamboman](https://github.com/williamboman)) - -### Changed - -- Rename package variable to pkg, as "package" is reserved. ([#399](https://github.com/thelounge/lounge/pull/399) by [@hogofwar](https://github.com/hogofwar)) -- Capitalise constructor Oidentd ([#396](https://github.com/thelounge/lounge/pull/396) by [@hogofwar](https://github.com/hogofwar)) -- Bump stylelint and update Travis CI configuration to include OSX builds and package caching ([#403](https://github.com/thelounge/lounge/pull/403) by [@xPaw](https://github.com/xPaw)) - -### Removed - -- Supersede `mkdirp` with `fs-extra` ([#390](https://github.com/thelounge/lounge/pull/390) by [@hogofwar](https://github.com/hogofwar)) -- Remove redundant variables ([#397](https://github.com/thelounge/lounge/pull/397) by [@hogofwar](https://github.com/hogofwar)) - -### Fixed - -- Save user's channels when they sort the channel list ([#401](https://github.com/thelounge/lounge/pull/401) by [@xPaw](https://github.com/xPaw)) -- Fix description of `host` and `bind` config options ([#378](https://github.com/thelounge/lounge/pull/378) by [@maxpoulin64](https://github.com/maxpoulin64)) - -## v2.0.0-pre.2 - 2016-06-09 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v2.0.0-pre.1...v2.0.0-pre.2) - -This pre-release adds a very, very long-awaited feature: auto-reconnection! It also extends our support of ident with oidentd, shows timestamps on small screens and fix bugs around notifications and sticky scroll. - -### Added - -- Enable auto reconnection ([#254](https://github.com/thelounge/lounge/pull/254) by [@xPaw](https://github.com/xPaw)) -- Add "!" modechar for admin ([#354](https://github.com/thelounge/lounge/pull/354) by [@omnicons](https://github.com/omnicons)) -- Add CI tool for Windows builds ([#367](https://github.com/thelounge/lounge/pull/367) by [@astorije](https://github.com/astorije)) -- Add support for oidentd spoofing ([#256](https://github.com/thelounge/lounge/pull/256) by [@maxpoulin64](https://github.com/maxpoulin64)) - -### Changed - -- Update Font Awesome to v4.6.3 ([#355](https://github.com/thelounge/lounge/pull/355) by [@MaxLeiter](https://github.com/MaxLeiter)) -- Do not hide timestamps on small viewports ([#376](https://github.com/thelounge/lounge/pull/376) by [@xPaw](https://github.com/xPaw)) -- Fetch Font Awesome from npm instead of embedded in repo ([#361](https://github.com/thelounge/lounge/pull/361) by [@astorije](https://github.com/astorije)) -- Cache npm modules on appveyor ([#381](https://github.com/thelounge/lounge/pull/381) by [@xPaw](https://github.com/xPaw)) -- Update eslint and enforce key-spacing ([#384](https://github.com/thelounge/lounge/pull/384) by [@xPaw](https://github.com/xPaw)) -- Use `npm-run-all` in npm scripts for testing and linting ([#375](https://github.com/thelounge/lounge/pull/375) by [@williamboman](https://github.com/williamboman)) -- Upload test results on appveyor builds ([#382](https://github.com/thelounge/lounge/pull/382) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Turn favicon red on page load if there are highlights ([#344](https://github.com/thelounge/lounge/pull/344) by [@xPaw](https://github.com/xPaw)) -- Do not send completely empty messages ([#345](https://github.com/thelounge/lounge/pull/345) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Make sure npm test script gets run on AppVeyor ([#372](https://github.com/thelounge/lounge/pull/372) by [@astorije](https://github.com/astorije)) -- Keep chat stickied to the bottom on resize ([#346](https://github.com/thelounge/lounge/pull/346) by [@maxpoulin64](https://github.com/maxpoulin64)) - -## v2.0.0-pre.1 - 2016-05-22 [Pre-release] - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.5.0...v2.0.0-pre.1) - -This is a pre-release to allow early adopters to use The Lounge with [`irc-framework`](https://github.com/kiwiirc/irc-framework) as our underlying IRC library instead of [`slate`](https://github.com/slate/slate-irc). This change itself solves a lot of issues and adds many features, most of them [listed here](https://github.com/thelounge/lounge/pull/167#issue-139286868): IRCv3 compliance, user feedback improvement, etc. - -It also adds WEBIRC support, a better server logging capability, a web app manifest, improves the sticky scroll, and fixes a ton of bugs. - -### Added - -- Log enabled capabilities ([#272](https://github.com/thelounge/lounge/pull/272) by [@xPaw](https://github.com/xPaw)) -- Add global logging helper ([#257](https://github.com/thelounge/lounge/pull/257) by [@xPaw](https://github.com/xPaw)) -- Add support for `~` home folder expansion ([#284](https://github.com/thelounge/lounge/pull/284) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Document supported node version ([#280](https://github.com/thelounge/lounge/pull/280) by [@xPaw](https://github.com/xPaw)) -- Add support for echo-message and znc.in/self-message caps ([#270](https://github.com/thelounge/lounge/pull/270) by [@xPaw](https://github.com/xPaw)) -- Implement WEBIRC ([#240](https://github.com/thelounge/lounge/pull/240) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Add `manifest.json` for nicer mobile experience ([#310](https://github.com/thelounge/lounge/pull/310) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Drop `slate-irc`, switch to `irc-framework` ([#167](https://github.com/thelounge/lounge/pull/167) by [@xPaw](https://github.com/xPaw)) -- Create a single helper function to write messages ([#266](https://github.com/thelounge/lounge/pull/266) by [@xPaw](https://github.com/xPaw)) -- Update dependencies ([#281](https://github.com/thelounge/lounge/pull/281) by [@xPaw](https://github.com/xPaw)) -- Improve sticky scroll ([#262](https://github.com/thelounge/lounge/pull/262) by [@xPaw](https://github.com/xPaw)) -- Change license link to point at our license file ([#290](https://github.com/thelounge/lounge/pull/290) by [@xPaw](https://github.com/xPaw)) -- Stricter eslint rule for curly brackets ([#291](https://github.com/thelounge/lounge/pull/291) by [@xPaw](https://github.com/xPaw)) -- Bump patch version of lodash to 4.11.2 ([#306](https://github.com/thelounge/lounge/pull/306) by [@astorije](https://github.com/astorije)) -- Minor wording changes for better clarity ([#305](https://github.com/thelounge/lounge/pull/305) by [@astorije](https://github.com/astorije)) -- Improve tests execution ([#260](https://github.com/thelounge/lounge/pull/260) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Update irc-framework ([#324](https://github.com/thelounge/lounge/pull/324) by [@xPaw](https://github.com/xPaw)) -- Do not ignore our handlebars plugins in ESLint ([#329](https://github.com/thelounge/lounge/pull/329) by [@xPaw](https://github.com/xPaw)) -- Improve nick highlights ([#327](https://github.com/thelounge/lounge/pull/327) by [@xPaw](https://github.com/xPaw)) -- CSS classes in themes for nick colors ([#325](https://github.com/thelounge/lounge/pull/325) by [@astorije](https://github.com/astorije)) -- Replace all concatenated paths with Node's path.join ([#307](https://github.com/thelounge/lounge/pull/307) by [@astorije](https://github.com/astorije)) - -### Removed - -- Do not increase font size on highlight in morning theme ([#321](https://github.com/thelounge/lounge/pull/321) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Only increase unread counter for whitelisted actions ([#273](https://github.com/thelounge/lounge/pull/273) by [@xPaw](https://github.com/xPaw)) -- Parse CTCP replies ([#278](https://github.com/thelounge/lounge/pull/278) by [@xPaw](https://github.com/xPaw)) -- Do not count your own messages as unread ([#279](https://github.com/thelounge/lounge/pull/279) by [@xPaw](https://github.com/xPaw)) -- Use lowercase global to avoid a deprecation warning in Node.js 6 ([`d9a0dd9`](https://github.com/thelounge/lounge/commit/d9a0dd9406e8fb22d7a5ee1ed4ed7aa8e5f0fa01) by [@xPaw](https://github.com/xPaw)) -- Do not display incorrect nick when switching to a non connected network ([#252](https://github.com/thelounge/lounge/pull/252) by [@xPaw](https://github.com/xPaw)) -- Keep autocompletion sort whenever user list updates ([#217](https://github.com/thelounge/lounge/pull/217) by [@xPaw](https://github.com/xPaw)) -- Make sure app does not crash when webirc is not defined in the configuration ([#294](https://github.com/thelounge/lounge/pull/294) by [@astorije](https://github.com/astorije)) -- Save user when parting channels ([#297](https://github.com/thelounge/lounge/pull/297) by [@xPaw](https://github.com/xPaw)) -- Add labels in connect window ([#300](https://github.com/thelounge/lounge/pull/300) by [@xPaw](https://github.com/xPaw)) -- Add missing `aria-label` on icon buttons ([#303](https://github.com/thelounge/lounge/pull/303) by [@astorije](https://github.com/astorije)) -- Fix unread counter not being formatted on page load ([#308](https://github.com/thelounge/lounge/pull/308) by [@xPaw](https://github.com/xPaw)) -- Fix wrong CSS for disabled colored nicknames on themes ([#318](https://github.com/thelounge/lounge/pull/318) by [@astorije](https://github.com/astorije)) -- Fix missing colors in action messages ([#317](https://github.com/thelounge/lounge/pull/317) by [@astorije](https://github.com/astorije)) -- Don't falsely report failed write if it didn't fail ([`e6990e0`](https://github.com/thelounge/lounge/commit/e6990e0fc7641d18a5bcbabddca1aacf2254ae52) by [@xPaw](https://github.com/xPaw)) -- Fix sending messages starting with a space ([#320](https://github.com/thelounge/lounge/pull/320) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix notifications in query windows ([#334](https://github.com/thelounge/lounge/pull/334) by [@xPaw](https://github.com/xPaw)) - -### Security - -- Restrict access to the home directory by default ([#205](https://github.com/thelounge/lounge/pull/205) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Update demo link to HTTPS ([#302](https://github.com/thelounge/lounge/pull/302) by [@MaxLeiter](https://github.com/MaxLeiter)) -- Add security headers to minimize XSS damage ([#292](https://github.com/thelounge/lounge/pull/292) by [@xPaw](https://github.com/xPaw)) -- Do not write user configs outside of the app's users directory ([#238](https://github.com/thelounge/lounge/pull/238) by [@williamboman](https://github.com/williamboman)) -- Don't check for existing password emptiness ([#315](https://github.com/thelounge/lounge/pull/315) by [@maxpoulin64](https://github.com/maxpoulin64)) - -## v1.5.0 - 2016-04-13 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.4.3...v1.5.0) - -With this release, administrators can now define a maximum size for channel history. -While this is not optimal nor the definitive solution, it aims at reducing stability issues where The Lounge would crash after filling up the server's memory. - -Other changes noticeable by users include removing custom print styles and preventing sequences of white spaces to collapse into one. - -### Added - -- Add config option to limit in-memory history size ([#243](https://github.com/thelounge/lounge/pull/243) by [@maxpoulin64](https://github.com/maxpoulin64)) - -### Changed - -- Do not parse link titles for IRC formatting ([#245](https://github.com/thelounge/lounge/pull/245) by [@xPaw](https://github.com/xPaw)) -- Display multiple white spaces properly ([#239](https://github.com/thelounge/lounge/pull/239) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Reword password prompt of `add` and `reset` CLI commands ([#230](https://github.com/thelounge/lounge/pull/230) by [@williamboman](https://github.com/williamboman)) - -### Removed - -- Remove print styles ([#228](https://github.com/thelounge/lounge/pull/228) by [@xPaw](https://github.com/xPaw)) - -## v1.4.3 - 2016-04-02 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.4.2...v1.4.3) - -This PR fixes a bug introduced in v1.3.0 which prevents deleting disconnected networks from users' configuration files. - -### Fixed - -- Fix not being able to remove networks from user config ([#233](https://github.com/thelounge/lounge/pull/233) by [@xPaw](https://github.com/xPaw)) - -## v1.4.2 - 2016-03-31 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.4.1...v1.4.2) - -This PR fixes a bug introduced in v1.4.1 causing timestamps to use most of the screen. - -### Fixed - -- Hide options will now remove the entire row ([#227](https://github.com/thelounge/lounge/pull/227) by [@xPaw](https://github.com/xPaw)) - -## v1.4.1 - 2016-03-28 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.4.0...v1.4.1) - -As of this release, running `/query nick` will simply open a chat window with user `nick`, instead of calling `whois` for this user. - -### Changed - -- Remove `join`, `nick` and `whois` inputs, they are cleanly handled by the server ([#208](https://github.com/thelounge/lounge/pull/208) by [@xPaw](https://github.com/xPaw)) -- Add a `/query` command that simply opens a query window ([#218](https://github.com/thelounge/lounge/pull/218) by [@xPaw](https://github.com/xPaw)) -- Disallow `/query` on non-nicks ([#221](https://github.com/thelounge/lounge/pull/221) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Fix message and topic text wrapping ([#215](https://github.com/thelounge/lounge/pull/215) by [@xPaw](https://github.com/xPaw)) -- Fix `/part` command ([#222](https://github.com/thelounge/lounge/pull/222) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Harden URL fetcher and don't crash on non-ASCII urls ([#219](https://github.com/thelounge/lounge/pull/219) by [@xPaw](https://github.com/xPaw)) - -## v1.4.0 - 2016-03-20 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.3.1...v1.4.0) - -Note that this release will reset users' notification settings to their defaults. This unfortunate side effect is the consequence of an improvement of how this setting is handled in the application. - -### Added - -- Add context menu when right-clicking on a sidebar item ([#9](https://github.com/thelounge/lounge/pull/9) by [@xPaw](https://github.com/xPaw)) -- Add tests for the `Chan#sortUsers` method ([#197](https://github.com/thelounge/lounge/pull/197) by [@astorije](https://github.com/astorije)) -- Add a very basic test for `Network#export` ([#198](https://github.com/thelounge/lounge/pull/198) by [@astorije](https://github.com/astorije)) -- Link to the demo from the IRC channel badge on the README ([#203](https://github.com/thelounge/lounge/pull/203) by [@Henni](https://github.com/Henni)) -- Add support for HTTP/2 ([#174](https://github.com/thelounge/lounge/pull/174) by [@xPaw](https://github.com/xPaw)) -- Support port in `/connect` command ([#210](https://github.com/thelounge/lounge/pull/210) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Update Handlebars to 4.0.5 ([#140](https://github.com/thelounge/lounge/pull/140) by [@xPaw](https://github.com/xPaw)) -- Update Socket.IO to 1.4.5 and use client library provided by the dependency ([#142](https://github.com/thelounge/lounge/pull/142) by [@xPaw](https://github.com/xPaw)) -- Update ESLint to 2.3.0 and add stricter rules ([#171](https://github.com/thelounge/lounge/pull/171) by [@xPaw](https://github.com/xPaw)) -- Mute color of the topic actions ([#151](https://github.com/thelounge/lounge/pull/151) by [@astorije](https://github.com/astorije)) -- Rename "badge" setting and rely on browser choice for desktop notifications ([#28](https://github.com/thelounge/lounge/pull/28) by [@lpoujol](https://github.com/lpoujol)) -- Invoke `handlebars` outside of `grunt` and generate a sourcemap ([#144](https://github.com/thelounge/lounge/pull/144) by [@xPaw](https://github.com/xPaw)) -- Make `whois` a client action template and improve its output ([#161](https://github.com/thelounge/lounge/pull/161) by [@xPaw](https://github.com/xPaw)) -- Handle commands in a better way and send unknown commands to the IRC server ([#154](https://github.com/thelounge/lounge/pull/154) by [@xPaw](https://github.com/xPaw)) -- Switch the Send button to a paper plane icon ([#182](https://github.com/thelounge/lounge/pull/182) by [@astorije](https://github.com/astorije)) -- Keep track of highlights when user is offline ([#190](https://github.com/thelounge/lounge/pull/190) by [@xPaw](https://github.com/xPaw)) -- Load input plugins at startup and call them directly when a command is received ([#191](https://github.com/thelounge/lounge/pull/191) by [@astorije](https://github.com/astorije)) -- Make defaults for socket.io transports consistent to use polling before websocket ([#202](https://github.com/thelounge/lounge/pull/202) by [@xPaw](https://github.com/xPaw)) -- Update all server dependencies to current stable versions ([#200](https://github.com/thelounge/lounge/pull/200) by [@xPaw](https://github.com/xPaw)) -- Update configuration file to reflect HTTP/2 support addition ([#206](https://github.com/thelounge/lounge/pull/206) by [@astorije](https://github.com/astorije)) -- Change close button behavior and add a dropdown context menu ([#184](https://github.com/thelounge/lounge/pull/184) by [@xPaw](https://github.com/xPaw)) -- Minor enhancements of the context menu UI ([#212](https://github.com/thelounge/lounge/pull/212) by [@astorije](https://github.com/astorije)) - -### Removed - -- Remove `string.contains` library ([#163](https://github.com/thelounge/lounge/pull/163) by [@xPaw](https://github.com/xPaw)) -- Remove Moment.js library from the client ([#183](https://github.com/thelounge/lounge/pull/183) by [@xPaw](https://github.com/xPaw)) -- Disabled emails from Travis CI on successful builds ([#172](https://github.com/thelounge/lounge/pull/172) by [@xPaw](https://github.com/xPaw)) -- Remove unnecessary operation when sorting users ([#193](https://github.com/thelounge/lounge/pull/193) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Make sure self messages are never highlighted and improve highlight lookup ([#157](https://github.com/thelounge/lounge/pull/157) by [@astorije](https://github.com/astorije)) -- Fix Send button style on Zenburn and Morning themes, introduced by this release ([#187](https://github.com/thelounge/lounge/pull/187) by [@astorije](https://github.com/astorije)) -- Make sure all close buttons in the sidebar have same weight ([#192](https://github.com/thelounge/lounge/pull/192) by [@astorije](https://github.com/astorije)) -- Disallow parting from lobbies ([#209](https://github.com/thelounge/lounge/pull/209) by [@xPaw](https://github.com/xPaw)) - -## v1.3.1 - 2016-03-05 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.3.0...v1.3.1) - -### Removed - -- Remove attempts to set file modes ([#117](https://github.com/thelounge/lounge/pull/117) by [@maxpoulin64](https://github.com/maxpoulin64)) - -### Fixed - -- Correctly handle inline channels in messages ([#128](https://github.com/thelounge/lounge/pull/128) by [@xPaw](https://github.com/xPaw)) -- Fix crash, introduced by this release ([#143](https://github.com/thelounge/lounge/pull/143) by [@xPaw](https://github.com/xPaw)) -- Fix highlighted actions and mute colors of some of the actions ([#47](https://github.com/thelounge/lounge/pull/47) by [@xPaw](https://github.com/xPaw)) -- Fix stripping multiple colors from notifications ([#145](https://github.com/thelounge/lounge/pull/145) by [@xPaw](https://github.com/xPaw)) -- Correctly display channel name in notifications ([#148](https://github.com/thelounge/lounge/pull/148) by [@xPaw](https://github.com/xPaw)) -- Fix hover effect on channels in topics ([#149](https://github.com/thelounge/lounge/pull/149) by [@xPaw](https://github.com/xPaw)) -- Add missing mode action to muted colors ([#150](https://github.com/thelounge/lounge/pull/150) by [@astorije](https://github.com/astorije)) - -## v1.3.0 - 2016-03-03 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.2.1...v1.3.0) - -### Added - -- Add hostmask in `join`/`part`/`quit` messages and move actions to templates ([#94](https://github.com/thelounge/lounge/pull/94) by [@xPaw](https://github.com/xPaw)) -- Add a section in the README explaining why a fork was created ([#95](https://github.com/thelounge/lounge/pull/95) by [@almet](https://github.com/almet)) -- Add the ability to let users change their password from the settings page ([#57](https://github.com/thelounge/lounge/pull/57) by [@diddledan](https://github.com/diddledan)) -- Add the ability to let users set custom CSS in their settings ([#83](https://github.com/thelounge/lounge/pull/83) by [@xPaw](https://github.com/xPaw)) -- Add notifications for channel invites ([#127](https://github.com/thelounge/lounge/pull/127) by [@astorije](https://github.com/astorije)) -- Allow locking network configuration ([#82](https://github.com/thelounge/lounge/pull/82) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Add target channel name in notifications ([#118](https://github.com/thelounge/lounge/pull/118) by [@astorije](https://github.com/astorije)) -- Bump `grunt-contrib-uglify` and pin versions of `grunt`-related dependencies ([#119](https://github.com/thelounge/lounge/pull/119) by [@astorije](https://github.com/astorije)) -- Switch to a power-off icon for logging out ([#131](https://github.com/thelounge/lounge/pull/131) by [@astorije](https://github.com/astorije)) - -### Removed - -- Remove auto-select on input fields ([#120](https://github.com/thelounge/lounge/pull/120) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Fix the "Show more" button being displayed over chat messages and message paddings when `join`/`part`/`quit` messages are hidden ([`b53e5c4`](https://github.com/thelounge/lounge/commit/b53e5c407c7ca90e9741791b4e0d927fb5f54ea1) by [@xPaw](https://github.com/xPaw)) -- Fix how highlights are handled and highlighted ([#91](https://github.com/thelounge/lounge/pull/91) by [@xPaw](https://github.com/xPaw)) -- Fix favicon highlight on Chrome and remove `Favico.js` library ([#100](https://github.com/thelounge/lounge/pull/100) by [@xPaw](https://github.com/xPaw)) -- Fix complete crash when refreshing a public instance, introduced by this release ([#125](https://github.com/thelounge/lounge/pull/125) by [@astorije](https://github.com/astorije)) -- Fix clickable "you" in the text of an `/invite`, introduced by this release ([#122](https://github.com/thelounge/lounge/pull/122) by [@xPaw](https://github.com/xPaw)) -- Fix minor issues with the main HTML file ([#134](https://github.com/thelounge/lounge/pull/134) by [@astorije](https://github.com/astorije)) -- Strip control codes from notifications ([#123](https://github.com/thelounge/lounge/pull/123) by [@xPaw](https://github.com/xPaw)) - -## v1.2.1 - 2016-02-26 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.2.0...v1.2.1) - -### Changed - -- Bump and pin mocha version ([#104](https://github.com/thelounge/lounge/pull/104) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Fix CSS selector syntax in channel message handler ([#102](https://github.com/thelounge/lounge/pull/102) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix fading channel name in sidebar of Crypto and Zenburn themes ([#105](https://github.com/thelounge/lounge/pull/105) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix `/invite` command broken by lodash bump ([#106](https://github.com/thelounge/lounge/pull/106) by [@JocelynDelalande](https://github.com/JocelynDelalande)) - -## v1.2.0 - 2016-02-24 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.1.1...v1.2.0) - -Note that this release will reset client-side settings to their defaults. Current users will have to re-set them in the settings page. This is [a conscious trade-off](https://github.com/thelounge/lounge/pull/70#issuecomment-186717859) as the fork is rather new and there are not many settings overall. - -### Added - -- Add support for the `/invite ` command ([#7](https://github.com/thelounge/lounge/pull/7) by [@xPaw](https://github.com/xPaw)) -- Add a command shorthand to invite in the current channel with `/invite ` ([#76](https://github.com/thelounge/lounge/pull/76) by [@astorije](https://github.com/astorije)) -- Add style linting for all CSS files in the repository ([#43](https://github.com/thelounge/lounge/pull/43) by [@xPaw](https://github.com/xPaw)) - -### Changed - -- Improve client performance by updating the users' list only when it's needed ([#58](https://github.com/thelounge/lounge/pull/58) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Let the badge counter hide with a fade-out ([#73](https://github.com/thelounge/lounge/pull/73) by [@xPaw](https://github.com/xPaw)) -- Update `lodash` dependency to the latest major version ([#38](https://github.com/thelounge/lounge/pull/38) by [@xPaw](https://github.com/xPaw)) -- Use `localStorage` instead of cookies for client-side settings storage ([#70](https://github.com/thelounge/lounge/pull/70) by [@xPaw](https://github.com/xPaw)) -- Replace Bootstrap's tooltips with CSS tooltips from GitHub's Primer ([#79](https://github.com/thelounge/lounge/pull/79) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Fade long channel names in the sidebar instead of breaking to another line ([#75](https://github.com/thelounge/lounge/pull/75) by [@maxpoulin64](https://github.com/maxpoulin64)) - -## v1.1.1 - 2016-02-19 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.1.0...v1.1.1) - -### Changed - -- Remove compiled assets and generate them at prepublish time ([#63](https://github.com/thelounge/lounge/pull/63) by [@astorije](https://github.com/astorije)) - -## v1.1.0 - 2016-02-19 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.0.2...v1.1.0) - -### Added - -- Allow The Lounge to be proxied behind a `/path/` URL ([#27](https://github.com/thelounge/lounge/pull/27) by [@gdamjan](https://github.com/gdamjan)) - -### Changed - -- Simplify a great deal the CONTRIBUTING file ([#40](https://github.com/thelounge/lounge/pull/40) by [@astorije](https://github.com/astorije)) -- Use a Font Awesome icon for the channel closing button ([#48](https://github.com/thelounge/lounge/pull/48) by [@xPaw](https://github.com/xPaw)) - -### Removed - -- Remove Node 0.10 from Travis CI ([#60](https://github.com/thelounge/lounge/pull/60) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Suppress deprecation warning for `moment().zone` ([#37](https://github.com/thelounge/lounge/pull/37) by [@deiu](https://github.com/deiu)) -- Fix a bug preventing the closing of a channel when the user was kicked out ([#34](https://github.com/thelounge/lounge/pull/34) by [@xPaw](https://github.com/xPaw)) - -## v1.0.2 - 2016-02-15 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.0.1...v1.0.2) - -### Changed - -- Remove `#foo` channel from the default configuration file ([#22](https://github.com/thelounge/lounge/pull/22) by [@astorije](https://github.com/astorije)) -- Change the Freenode URL to `chat.freenode.net` in the default configuration file ([#13](https://github.com/thelounge/lounge/pull/13) by [@dubzi](https://github.com/dubzi)) -- Ensure all `.js` files are linted ([#42](https://github.com/thelounge/lounge/pull/42) by [@williamboman](https://github.com/williamboman)) - -### Fixed - -- Hide the user list button on a server or private message window ([#32](https://github.com/thelounge/lounge/pull/32) by [@MaxLeiter](https://github.com/MaxLeiter)) -- Correctly sort the user list whenever a user joins ([#33](https://github.com/thelounge/lounge/pull/33) by [@xPaw](https://github.com/xPaw)) - -## v1.0.1 - 2016-02-14 - -[See the full changelog](https://github.com/thelounge/lounge/compare/v1.0.0...v1.0.1) - -### Changed - -- In the change log, use a permanent URL to link the previous history of The Lounge to Shout ([#12](https://github.com/thelounge/lounge/pull/12) by [@xPaw](https://github.com/xPaw)) -- Update some dependencies and pin versions ([#8](https://github.com/thelounge/lounge/pull/8) by [@xPaw](https://github.com/xPaw)) - -### Fixed - -- Add missing form methods that were causing LastPass to trigger a warning ([#19](https://github.com/thelounge/lounge/pull/19) by [@maxpoulin64](https://github.com/maxpoulin64)) -- Fix comments in the configuration file ([#1](https://github.com/thelounge/lounge/pull/1) by [@FryDay](https://github.com/FryDay)) - -## v1.0.0 - 2016-02-12 - -[See the full changelog](https://github.com/thelounge/lounge/compare/baadc3df3534fb22515a8c2ea29218fbbc1228b4...v1.0.0) - -This is the first release of **The Lounge**, picking up where Shout `v0.53.0` left off! - -### Added - -- Enable notifications on all messages, which can be controlled in the settings ([#540](https://github.com/erming/shout/pull/540) by [@nickel715](https://github.com/nickel715)) -- Add Travis CI and David DM badges on the README ([#465](https://github.com/erming/shout/pull/465) by [@astorije](https://github.com/astorije)) -- Emit sent notice back to the user ([#590](https://github.com/erming/shout/pull/590) by [@xPaw](https://github.com/xPaw)) -- Send user agent with link expander requests ([#608](https://github.com/erming/shout/pull/608) by [@xPaw](https://github.com/xPaw)) -- Add a `.gitattributes` file to normalize line endings ([#610](https://github.com/erming/shout/pull/610) by [@xPaw](https://github.com/xPaw)) -- Style scrollbars (WebKit only) ([#593](https://github.com/erming/shout/pull/593) by [@xPaw](https://github.com/xPaw)) -- Add a badge to display the IRC channel at the top ([#599](https://github.com/erming/shout/pull/599) by [@astorije](https://github.com/astorije)) -- Rotate `part`/`quit` icon for new action display ([#617](https://github.com/erming/shout/pull/617) by [@MaxLeiter](https://github.com/MaxLeiter)) - -### Changed - -- Update slate-irc to v0.8.1 ([#597](https://github.com/erming/shout/pull/597) by [@xPaw](https://github.com/xPaw)) -- Limit maximum height of inline images ([#598](https://github.com/erming/shout/pull/598) by [@xPaw](https://github.com/xPaw)) -- Use a single function to process and render messages ([#596](https://github.com/erming/shout/pull/596) by [@xPaw](https://github.com/xPaw)) -- Render user actions separately ([#588](https://github.com/erming/shout/pull/588) by [@xPaw](https://github.com/xPaw)) -- Simply parse all 0-99 IRC colors ([#609](https://github.com/erming/shout/pull/609) by [@xPaw](https://github.com/xPaw)) -- Tag notifications to reduce notification spam ([#418](https://github.com/erming/shout/pull/418) by [@williamboman](https://github.com/williamboman)) -- Change all mentions of Shout to the new name: The Lounge ([#2](https://github.com/thelounge/lounge/pull/2) by [@astorije](https://github.com/astorije)) - -### Fixed - -- Fix initial copyright year in the LICENSE notice ([#591](https://github.com/erming/shout/pull/591) by [@pra85](https://github.com/pra85)) -- Fix wrong color class on Zenburn style ([#595](https://github.com/erming/shout/pull/595) by [@astorije](https://github.com/astorije)) -- Run new topic through parser when it is updated ([#587](https://github.com/erming/shout/pull/587) by [@xPaw](https://github.com/xPaw)) -- Fix several things on Morning and Zenburn themes ([#605](https://github.com/erming/shout/pull/605) by [@xPaw](https://github.com/xPaw)) -- Fix word wrap on firefox ([#570](https://github.com/erming/shout/pull/570) by [@YaManicKill](https://github.com/YaManicKill)) -- Change user buttons to `a` links, allowing highlighting on Firefox ([#571](https://github.com/erming/shout/pull/571) and [#574](https://github.com/erming/shout/pull/574) by [@YaManicKill](https://github.com/YaManicKill)) - ---- - -All previous changes can be found on [Shout's CHANGELOG, starting at `v0.53.0`](https://github.com/erming/shout/blob/35587f3c35d0a8ac78a2495934ff9eaa8f1aa71c/CHANGELOG.md#0530--2016-01-07). diff --git a/sources/CONTRIBUTING.md b/sources/CONTRIBUTING.md deleted file mode 100644 index f3da308..0000000 --- a/sources/CONTRIBUTING.md +++ /dev/null @@ -1,37 +0,0 @@ -## Contributing - -Welcome to The Lounge, it's great to have you here! We thank you in advance for -your contributions. - -### I have a question - -- Find us on the Freenode channel `#thelounge`. You might not get an answer - right away, but this channel is full of nice people who will be happy to - help you. - -### I want to report a bug - -- Look at the [open and closed - issues](https://github.com/thelounge/lounge/issues?q=is%3Aissue) to see if - this was not already discussed before. If you can't see any, feel free to - [open a new issue](https://github.com/thelounge/lounge/issues/new). - -### I want to contribute to the code - -- Make sure to discuss your ideas with the community in an - [issue](https://github.com/thelounge/lounge/issues) or on the IRC channel. -- Take a look at the open issues labeled as [`help wanted`](https://github.com/thelounge/lounge/issues?q=is%3Aopen+is%3Aissue+label%3Abug+label%3A%22help+wanted%22) - if you want to help without having a specific idea in mind. -- Make sure that your PRs do not contain unnecessary commits or merge commits. - [Squash commits](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) - whenever possible. -- [Rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) (instead of - merge) outdated PRs on the `master` branch. -- Give extra care to your commit messages. Use the [imperative present - tense](https://git-scm.com/book/ch5-2.html#Commit-Guidelines) and [follow Tim - Pope's guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). -- Each PR will be reviewed by at least two different project maintainers. You - can read more about this in the [maintainers' -corner](https://github.com/thelounge/lounge/wiki/Maintainers'-corner). -- Please document any relevant changes in the documentation that can be found - [in its own repository](https://github.com/thelounge/thelounge.github.io). diff --git a/sources/LICENSE b/sources/LICENSE deleted file mode 100644 index 6f9d321..0000000 --- a/sources/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 All contributors to The Lounge -Copyright (c) 2014 Mattias Erming and contributors, as part of Shout. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/sources/README.md b/sources/README.md deleted file mode 100644 index 54ea3c3..0000000 --- a/sources/README.md +++ /dev/null @@ -1,83 +0,0 @@ -[![#thelounge IRC channel on freenode](https://img.shields.io/badge/irc%20channel-%23thelounge%20on%20freenode-blue.svg)](https://avatar.playat.ch:1000/) -[![npm version](https://img.shields.io/npm/v/thelounge.svg)](https://www.npmjs.org/package/thelounge) -[![Travis CI Build Status](https://travis-ci.org/thelounge/lounge.svg?branch=master)](https://travis-ci.org/thelounge/lounge) -[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/deymtp0lldq78s8t/branch/master?svg=true)](https://ci.appveyor.com/project/astorije/lounge/branch/master) -[![Dependency Status](https://david-dm.org/thelounge/lounge.svg)](https://david-dm.org/thelounge/lounge) -[![devDependency Status](https://david-dm.org/thelounge/lounge/dev-status.svg)](https://david-dm.org/thelounge/lounge?type=dev) - -# The Lounge - -__What is it?__ - -The Lounge is a web IRC client that you host on your own server. - -*This is the official, community-managed fork of @erming's great initiative, the [Shout](https://github.com/erming/shout) project.* - -__What features does it have?__ - -- Multiple user support -- Stays connected even when you close the browser -- Connect from multiple devices at once -- Responsive layout — works well on your smartphone -- _.. and more!_ - -__Why the fork?__ - -We felt that the original [Shout](https://github.com/erming/shout) project -"stagnated" a little because its original author wanted it to remain his pet -project (which is a perfectly fine thing!). - -A bunch of people, excited about doing things a bit differently than the upstream -project forked it under a new name: “The Lounge”. - -This fork aims to be community managed, meaning that the decisions are taken -in a collegial fashion, and that a bunch of maintainers should be able to make -the review process quicker and more streamlined. - -## Installation and usage - -The Lounge requires [Node.js](https://nodejs.org/) v4 or more recent. - -### Running stable releases from npm (recommended) - -Run this in a terminal to install (or upgrade) the latest stable release from -[npm](https://www.npmjs.com/): - -```sh -[sudo] npm install -g thelounge -``` - -When installation is complete, run: - -```sh -lounge start -``` - -For more information, read the [documentation](https://thelounge.github.io/docs/), [wiki](https://github.com/thelounge/lounge/wiki), or run: - -```sh -lounge --help -``` - -### Running from source - -The following commands install the development version of The Lounge. A word of -caution: while it is the most recent codebase, this is not production-ready! - -```sh -git clone https://github.com/thelounge/lounge.git -cd lounge -npm install -npm start -``` - -## Development setup - -Simply follow the instructions to run The Lounge from source above, on your own -fork. - -Before submitting any change, make sure to: - -- Read the [Contributing instructions](https://github.com/thelounge/lounge/blob/master/CONTRIBUTING.md#contributing) -- Run `npm test` to execute linters and test suite -- Run `npm run build` if you change or add anything in `client/js/libs` or `client/views` diff --git a/sources/appveyor.yml b/sources/appveyor.yml deleted file mode 100644 index e63ba59..0000000 --- a/sources/appveyor.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# http://www.appveyor.com/docs/appveyor-yml - -# Build version format -version: "{build}" - -# Do not build on tags (GitHub only) -skip_tags: true - -environment: - nodejs_version: '4' - -install: - - ps: Install-Product node $env:nodejs_version - - npm install - - npm install mocha-appveyor-reporter - - echo --reporter mocha-appveyor-reporter >> test/mocha.opts - -test_script: - - node --version - - npm --version - - npm test - -# cache npm modules -cache: - - '%AppData%/npm-cache' - -# Don't actually build -build: off diff --git a/sources/client/audio/pop.ogg b/sources/client/audio/pop.ogg deleted file mode 100644 index 1fe623f..0000000 Binary files a/sources/client/audio/pop.ogg and /dev/null differ diff --git a/sources/client/css/bootstrap.css b/sources/client/css/bootstrap.css deleted file mode 100644 index e1d5b22..0000000 --- a/sources/client/css/bootstrap.css +++ /dev/null @@ -1,1185 +0,0 @@ -/*! - * Bootstrap v3.2.0 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ - -/*! - * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=242b77ebf84da0cd12cf) - * Config saved to config.json and https://gist.github.com/242b77ebf84da0cd12cf - */ -/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ -html { - font-family: sans-serif; - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - font-size: 2em; - margin: 0.67em 0; -} -mark { - background: #ff0; - color: #000; -} -small { - font-size: 80%; -} -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} -sup { - top: -0.5em; -} -sub { - bottom: -0.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - -moz-box-sizing: content-box; - box-sizing: content-box; - height: 0; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - color: inherit; - font: inherit; - margin: 0; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-appearance: textfield; - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - box-sizing: content-box; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} -legend { - border: 0; - padding: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-collapse: collapse; - border-spacing: 0; -} -td, -th { - padding: 0; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333333; - background-color: #ffffff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #428bca; - text-decoration: none; -} -a:hover, -a:focus { - color: #2a6496; - text-decoration: underline; -} -a:focus { - outline: thin dotted; - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive { - display: block; - width: 100% \9; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - padding: 4px; - line-height: 1.42857143; - background-color: #ffffff; - border: 1px solid #dddddd; - border-radius: 4px; - -webkit-transition: all 0.2s ease-in-out; - -o-transition: all 0.2s ease-in-out; - transition: all 0.2s ease-in-out; - display: inline-block; - width: 100% \9; - max-width: 100%; - height: auto; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eeeeee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -cite { - font-style: normal; -} -mark, -.mark { - background-color: #fcf8e3; - padding: .2em; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777777; -} -.text-primary { - color: #428bca; -} -a.text-primary:hover { - color: #3071a9; -} -.text-success { - color: #3c763d; -} -a.text-success:hover { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #428bca; -} -a.bg-primary:hover { - background-color: #3071a9; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eeeeee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - list-style: none; - margin-left: -5px; -} -.list-inline > li { - display: inline-block; - padding-left: 5px; - padding-right: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - clear: left; - text-align: right; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eeeeee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - border-right: 5px solid #eeeeee; - border-left: 0; - text-align: right; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -blockquote:before, -blockquote:after { - content: ""; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -.container { - margin-right: auto; - margin-left: auto; - padding-left: 15px; - padding-right: 15px; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - margin-right: auto; - margin-left: auto; - padding-left: 15px; - padding-right: 15px; -} -.row { - margin-left: -15px; - margin-right: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-left: 15px; - padding-right: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0%; -} -@media (min-width: 480px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0%; - } -} -.fade { - opacity: 0; - -webkit-transition: opacity 0.15s linear; - -o-transition: opacity 0.15s linear; - transition: opacity 0.15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition: height 0.35s ease; - -o-transition: height 0.35s ease; - transition: height 0.35s ease; -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after { - content: " "; - display: table; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after { - clear: both; -} -.center-block { - display: block; - margin-left: auto; - margin-right: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; - visibility: hidden !important; -} -.affix { - position: fixed; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 479px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 479px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 479px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 479px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (max-width: 479px) { - .hidden-xs { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} diff --git a/sources/client/css/fonts/Lato-700/LICENSE.txt b/sources/client/css/fonts/Lato-700/LICENSE.txt deleted file mode 100755 index 98383e3..0000000 --- a/sources/client/css/fonts/Lato-700/LICENSE.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/sources/client/css/fonts/Lato-700/Lato-700.woff b/sources/client/css/fonts/Lato-700/Lato-700.woff deleted file mode 100755 index 66c8242..0000000 Binary files a/sources/client/css/fonts/Lato-700/Lato-700.woff and /dev/null differ diff --git a/sources/client/css/fonts/Lato-700/Lato-700.woff2 b/sources/client/css/fonts/Lato-700/Lato-700.woff2 deleted file mode 100755 index a9ffeae..0000000 Binary files a/sources/client/css/fonts/Lato-700/Lato-700.woff2 and /dev/null differ diff --git a/sources/client/css/fonts/Lato-regular/LICENSE.txt b/sources/client/css/fonts/Lato-regular/LICENSE.txt deleted file mode 100755 index 98383e3..0000000 --- a/sources/client/css/fonts/Lato-regular/LICENSE.txt +++ /dev/null @@ -1,93 +0,0 @@ -Copyright (c) 2010-2014 by tyPoland Lukasz Dziedzic (team@latofonts.com) with Reserved Font Name "Lato" - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/sources/client/css/fonts/Lato-regular/Lato-regular.woff b/sources/client/css/fonts/Lato-regular/Lato-regular.woff deleted file mode 100755 index fe27504..0000000 Binary files a/sources/client/css/fonts/Lato-regular/Lato-regular.woff and /dev/null differ diff --git a/sources/client/css/fonts/Lato-regular/Lato-regular.woff2 b/sources/client/css/fonts/Lato-regular/Lato-regular.woff2 deleted file mode 100755 index c83fe95..0000000 Binary files a/sources/client/css/fonts/Lato-regular/Lato-regular.woff2 and /dev/null differ diff --git a/sources/client/css/fonts/inconsolatag.woff b/sources/client/css/fonts/inconsolatag.woff deleted file mode 100644 index d5ac16f..0000000 Binary files a/sources/client/css/fonts/inconsolatag.woff and /dev/null differ diff --git a/sources/client/css/style.css b/sources/client/css/style.css deleted file mode 100644 index 761b6f5..0000000 --- a/sources/client/css/style.css +++ /dev/null @@ -1,1788 +0,0 @@ -@font-face { - font-family: "Lato"; - font-weight: 400; - font-style: normal; - src: - local("Lato Regular"), - local("Lato-regular"), - url("fonts/Lato-regular/Lato-regular.woff2") format("woff2"), - url("fonts/Lato-regular/Lato-regular.woff") format("woff"); -} - -@font-face { - font-family: "Lato"; - font-weight: 700; - font-style: normal; - src: - local("Lato Bold"), - local("Lato-700"), - url("fonts/Lato-700/Lato-700.woff2") format("woff2"), - url("fonts/Lato-700/Lato-700.woff") format("woff"); -} - -@font-face { - font-family: "FontAwesome"; - font-weight: normal; - font-style: normal; - src: - url("../fonts/fontawesome-webfont.woff2?v=4.6.3") format("woff2"), - url("../fonts/fontawesome-webfont.woff?v=4.6.3") format("woff"); -} - -html, -body { - height: 100%; -} - -body { - background: #455164; - color: #222; - font: 16px Lato, sans-serif; - margin: 0; - - /** - * Disable pull-to-refresh on mobile that conflicts with scrolling the message list. - * See http://stackoverflow.com/a/29313685/1935861 - */ - overflow-y: hidden; -} - -a { - transition: opacity .2s; -} - -a:hover { - text-decoration: none; - opacity: .8; -} - -h1, -h2 { - font: inherit; - line-height: inherit; - margin: 0; -} - -h1.title { - margin-bottom: 10px; -} - -input { - outline: 0; -} - -button { - border: none; - background: none; - margin: 0; - outline: none; - padding: 0; -} - -.btn { - border: 2px solid #84ce88; - border-radius: 3px; - color: #84ce88; - display: inline-block; - font-size: 12px; - font-weight: bold; - letter-spacing: 1px; - margin-bottom: 10px; - padding: 9px 17px; - text-transform: uppercase; - transition: background .2s, border-color .2s, color .2s; - word-spacing: 3px; -} - -.btn:disabled, -.btn:hover { - background: #84ce88; - color: #fff; -} - -.btn:active { - box-shadow: none; - opacity: .8; -} - -.btn:disabled { - opacity: .6; -} - -.container { - margin: 80px auto; - max-width: 480px; - overflow: hidden; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - padding: 0 30px; -} - -::-moz-placeholder { - color: rgba(0, 0, 0, .35); - opacity: 1; -} - -::-webkit-input-placeholder { - color: rgba(0, 0, 0, .35); -} - -:-ms-input-placeholder { - color: rgba(0, 0, 0, .35) !important; -} - -/* Icons */ - -#viewport .lt:before, -#viewport .rt:before, -#chat button.menu:before, -#sidebar .chan:before, -#chat .title:before, -#footer .icon, -#chat .count:before, -#settings #play:before, -#form #submit:before, -#chat .invite .from:before, -#chat .join .from:before, -#chat .kick .from:before, -#chat .part .from:before, -#chat .quit .from:before, -#chat .topic .from:before, -#chat .mode .from:before, -#chat .ctcp .from:before, -#chat .whois .from:before, -#chat .nick .from:before, -#chat .action .from:before, -.context-menu-item:before, -#nick button:before { - font: normal normal normal 14px/1 FontAwesome; - font-size: inherit; /* Can't have font-size inherit on line above, so need to override */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -#viewport .lt:before { content: "\f0c9"; /* http://fontawesome.io/icon/bars/ */ } -#viewport .rt:before { content: "\f0c0"; /* http://fontawesome.io/icon/users/ */ } -#chat button.menu:before { content: "\f142"; /* http://fontawesome.io/icon/ellipsis-v/ */ } - -.context-menu-user:before { content: "\f007"; /* http://fontawesome.io/icon/user/ */ } -.context-menu-chan:before { content: "\f0f6"; /* http://fontawesome.io/icon/file-text-o/ */ } -.context-menu-close:before { content: "\f00d"; /* http://fontawesome.io/icon/times/ */ } - -#sidebar .chan.lobby:before, -#chat .lobby .title:before { content: "\f0a0"; /* http://fontawesome.io/icon/hdd-o/ */ } - -#sidebar .chan.query:before, -#chat .query .title:before { content: "\f0e6"; /* http://fontawesome.io/icon/comments-o/ */ } - -#sidebar .chan.channel:before, -#chat .channel .title:before { content: "\f0f6"; /* http://fontawesome.io/icon/file-text-o/ */ } - -#sidebar .chan.special:before, -#chat .special .title:before { content: "\f03a"; /* http://fontawesome.io/icon/list/ */ } - -#footer .sign-in:before { content: "\f023"; /* http://fontawesome.io/icon/lock/ */ } -#footer .connect:before { content: "\f067"; /* http://fontawesome.io/icon/plus/ */ } -#footer .settings:before { content: "\f013"; /* http://fontawesome.io/icon/cog/ */ } -#footer .sign-out:before { content: "\f011"; /* http://fontawesome.io/icon/power-off/ */ } - -#form #submit:before { content: "\f1d8"; /* http://fontawesome.io/icon/paper-plane/ */ } - -#chat .invite .from:before { - content: "\f003"; /* http://fontawesome.io/icon/envelope-o/ */ - color: #2ecc40; -} - -#chat .part .from:before, -#chat .quit .from:before { - content: "\f08b"; /* http://fontawesome.io/icon/sign-out/ */ - color: #ff4136; - display: inline-block; - -webkit-transform: rotate(180deg); - transform: rotate(180deg); -} - -#chat .topic .from:before { - content: "\f0a1"; /* http://fontawesome.io/icon/bullhorn/ */ - color: #2ecc40; -} - -#chat .mode .from:before { - content: "\f05a"; /* http://fontawesome.io/icon/info-circle/ */ - color: #2ecc40; -} - -#chat .ctcp .from:before { - content: "\f0f6"; /* http://fontawesome.io/icon/file-text-o/ */ -} - -#chat .whois .from:before { - content: "\f007"; /* http://fontawesome.io/icon/user/ */ - color: #2ecc40; -} - -#chat .nick .from:before { - content: "\f007"; /* http://fontawesome.io/icon/user/ */ - color: #2ecc40; -} - -#chat .join .from:before { - content: "\f090"; /* http://fontawesome.io/icon/sign-in/ */ - color: #2ecc40; -} - -#chat .kick .from:before { - content: "\f05e"; /* http://fontawesome.io/icon/ban/ */ - color: #ff4136; -} - -#chat .action .from:before { - content: "\f005"; /* http://fontawesome.io/icon/star/ */ -} - -#chat .count:before { - color: #cfcfcf; - content: "\f002"; /* http://fontawesome.io/icon/search/ */ - position: absolute; - right: 18px; - font-size: 14px; - line-height: 50px; -} - -#settings #play:before { - content: "\f028"; /* http://fontawesome.io/icon/volume-up/ */ - margin-right: 9px; -} - -#set-nick:before { - content: "\f040"; /* http://fontawesome.io/icon/pencil/ */ -} - -#submit-nick:before { - content: "\f00c"; /* http://fontawesome.io/icon/check/ */ -} - -#cancel-nick:before { - content: "\f00d"; /* http://fontawesome.io/icon/times/ */ -} - -/* End icons */ - -#wrap { - height: 100%; - overflow: hidden; -} - -#viewport { - height: 100%; - transition: transform 160ms, -webkit-transform 160ms; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-perspective: 1000; - perspective: 1000; -} - -#viewport.menu-open { - -webkit-transform: translate3d(220px, 0, 0); - transform: translate3d(220px, 0, 0); -} - -#viewport.menu-dragging { - transition: none !important; -} - -#viewport.menu-open .messages { - pointer-events: none; -} - -#viewport .lt, -#viewport .rt, -#chat button.menu { - color: #ccc; - display: none; - float: left; - font-size: 14px; - line-height: 1; - height: 36px; - margin: 6px 12px 0 -12px; - width: 36px; -} - -#viewport .lt { - position: relative; -} - -/* Notification dot on the top right corner of the menu icon */ -#viewport .lt:after { - content: ""; - position: absolute; - top: 9px; - right: 7px; - background-color: #e74c3c; - width: 10px; - height: 10px; - border-radius: 50%; - border: 2px solid white; - opacity: 0; - transition: opacity .2s; -} - -#viewport .lt.notified:after { - opacity: 1; -} - -#viewport .rt-tooltip { - float: right; -} - -#viewport .rt { - display: block; - margin: 6px -12px 0 0; -} - -#chat button.menu { - display: block; - float: right; - margin: 6px -8px 0 12px; -} - -#viewport.rt #chat .sidebar { - -webkit-transform: translate3d(180px, 0, 0); - transform: translate3d(180px, 0, 0); -} - -#sidebar { - bottom: 48px; - left: 0; - overflow: auto; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - position: absolute; - top: 0; - width: 220px; -} - -#sidebar button, -#sidebar .chan, -#sidebar .sign-out { - border: 1px solid transparent; - border-radius: 2px; - color: #99a2b4; - cursor: pointer; - font-size: 14px; -} - -#sidebar button:hover, -#sidebar .chan:hover, -#sidebar .active { - color: #fff; -} - -#sidebar .networks { - padding: 20px 30px 0; -} - -#sidebar .networks:empty { - padding: 0; -} - -#sidebar .network, -#sidebar .network-placeholder { - margin-bottom: 30px; -} - -#sidebar .empty { - color: #9ca5b4; - line-height: 1.6; - font-size: 12px; - margin-top: 20px; - padding: 20px 40px; - text-align: center; -} - -#sidebar .chan, -#sidebar .chan-placeholder { - display: block; - margin: 1px -10px; - padding: 6px 10px 8px 36px; - position: relative; - text-align: left; - transition: color .2s; - width: 180px; -} - -#sidebar .chan-placeholder { - padding-bottom: 10px; -} - -#sidebar .chan:first-child { - color: #84ce88; - font-size: 15px; - font-weight: bold; -} - -#sidebar .chan:first-child:hover, -#sidebar .chan:first-child.active { - color: #c0f8c3; -} - -#sidebar .chan:before, -#chat .title:before { - float: left; - margin-top: 3px; - margin-right: 12px; - text-align: center; -} - -#sidebar .chan:before { - position: absolute; - top: 4px; - left: 10px; -} - -#chat .title:before { - margin-top: 17px; -} - -#sidebar .chan .name { - position: relative; - z-index: 0; - display: block; - overflow: hidden; - white-space: nowrap; - margin-right: 5px; -} - -#sidebar .chan .name:after { - position: absolute; - top: 0; - right: 0; - bottom: 0; - width: 20px; - background: linear-gradient(to right, rgba(69, 81, 100, 0) 0%, rgba(69, 81, 100, 1) 100%); - content: " "; -} - -#sidebar .badge { - background: rgba(255, 255, 255, .06); - border-radius: 3px; - color: #afb6c0; - font-size: 10px; - margin-top: 1px; - margin-right: -5px; - margin-left: 5px; - padding: 3px 6px; - float: right; - transition: opacity .2s, background-color .2s, color .2s; -} - -#sidebar .badge.highlight { - background: #fff; - color: #49505a; -} - -#sidebar .chan.active .badge, -#sidebar .badge:empty { - opacity: 0; -} - -#sidebar .close { - border-radius: 3px; - margin-right: 5px; - visibility: hidden; - opacity: 0; - position: absolute; - z-index: 2; - right: 0; - transition: opacity .2s, background-color .2s; -} - -#sidebar .close:before { - font-size: 18px; - font-weight: normal; - display: inline-block; - line-height: 18px; - width: 18px; - height: 18px; - text-align: center; - content: "×"; - color: #fff; -} - -#sidebar .chan.active .close { - visibility: visible; - opacity: .4; -} - -#sidebar .chan.active .close:hover { - background-color: rgba(0, 0, 0, .1); - opacity: 1; -} - -#sidebar .tse-scrollbar { - top: 2px; - right: 3px; -} - -#sidebar, -#footer { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -#footer { - background: rgba(0, 0, 0, .06); - bottom: 0; - height: 45px; - left: 0; - font-size: 14px; - line-height: 45px; - position: absolute; - text-align: center; - width: 220px; -} - -#footer button.active { - color: #fff; -} - -#footer .icon { - color: #9ca5b4; - display: inline-block; - line-height: 34px; - padding: 0 12px; -} - -.signed-out #footer .sign-in { - display: inline-block; -} - -.signed-out #footer .connect, -.signed-out #footer .sign-out { - display: none; -} - -.public #footer .sign-in, -.public #footer .sign-out { - display: none; -} - -#footer .sign-in { - display: none; -} - -#main { - bottom: 0; - left: 220px; - position: absolute; - right: 0; - top: 0; - display: -webkit-flex; - display: flex; - -webkit-flex-direction: column; - flex-direction: column; -} - -.signed-out #main { - left: 0; /* Hide the sidebar when user is signed out */ -} - -#header { - display: none; - height: 40px; - position: absolute; - top: 0; - width: 100%; -} - -#windows label { - font-size: 14px; -} - -#windows .input { - background-color: white; - border: 1px solid #cdd3da; - border-radius: 2px; - color: #222; - font-size: 14px; - margin: 2px 0; - margin-bottom: 10px; - outline: 0; - padding: 8px 10px; - transition: border-color .2s; - width: 100%; -} - -#windows select.input { - height: 35px; -} - -#user-specified-css-input { - resize: vertical; -} - -#windows .input:hover, -#windows .input:focus { - border-color: #79838c; -} - -#windows .window { - background: #fff; - bottom: 0; - display: none; - left: 0; - position: absolute; - right: 0; - top: 0; - overflow-y: auto; - -webkit-overflow-scrolling: touch; -} - -#windows .window h1 { - font-size: 36px; -} - -#windows .window h2 { - border-bottom: 1px solid #eee; - color: #7f8c8d; - font-size: 22px; - margin: 30px 0 10px; - padding-bottom: 7px; -} - -#windows .active { - display: block; -} - -#windows .header { - border-bottom: 1px solid #e7e7e7; - line-height: 48px; - height: 48px; - padding: 0 20px; - overflow: hidden; -} - -#windows .header .title { - font-size: 14px; -} - -#windows .header .topic { - color: #777; - margin-left: 8px; - word-break: break-all; -} - -#windows .window .header { - display: none; -} - -#chat-container, -#chat .chan { - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; -} - -#windows #chat-container.active { - display: -webkit-flex; - display: flex; - -webkit-flex-direction: column; - flex-direction: column; -} - -#chat { - position: relative; - overflow: hidden; - -webkit-flex: 1; - flex: 1; -} - -#chat .chan { - display: none; -} - -#chat .chan.active { - display: block; -} - -#windows .header .topic, -.messages .msg, -.sidebar { - font: 12px Consolas, Menlo, Monaco, "Lucida Console", "DejaVu Sans Mono", "Courier New", monospace; - line-height: 1.4; -} - -#windows #chat .header { - display: block; -} - -#chat .chat, -#chat .sidebar { - top: 48px; -} - -#chat .chat { - bottom: 0; - left: 0; - overflow: auto; - -webkit-overflow-scrolling: touch; - position: absolute; - right: 180px; -} - -#chat .special { - bottom: -47px; -} - -#viewport.rt .chat { - right: 0; -} - -#chat .sidebar { - background: #fff; - border-left: 1px solid #e7e7e7; - bottom: 0; - position: absolute; - right: 0; - width: 180px; - transition: all .4s; - -webkit-transform: translateZ(0); - transform: translateZ(0); - -webkit-perspective: 1000; - perspective: 1000; -} - -#chat .lobby .chat, -#chat .special .chat, -#chat .query .chat { - right: 0; -} - -#chat .lobby .sidebar, -#chat .special .sidebar, -#chat .query .sidebar { - display: none; -} - -#chat .show-more { - display: none; - padding: 10px; - padding-bottom: 0; - width: 100%; -} - -#chat .show-more-button { - background: #f4f4f4; - background-image: linear-gradient(#f4f4f4, #ececec); - border: 1px solid #d7d7d7; - border-bottom-color: #b7b7b7; - border-radius: 2px; - color: #555; - font-size: 12px; - height: 34px; - line-height: 0; - width: 100%; -} - -#chat .show-more-button:hover { - opacity: 1; -} - -#chat .messages { - display: table; - table-layout: fixed; - width: 100%; - padding: 10px 0; -} - -#chat .msg { - word-wrap: break-word; - word-break: break-word; /* Webkit-specific */ -} - -#chat .unread-marker { - position: relative; - text-align: center; - opacity: .5; - margin: 0 10px; - z-index: 0; - font-weight: bold; - font-size: 12px; -} - -#chat .unread-marker:before { - position: absolute; - z-index: -1; - content: ""; - left: 0; - right: 0; - top: 50%; - border-top: 1px solid #e74c3c; -} - -#chat .unread-marker-text:before { - content: "New messages"; - background-color: white; - color: #e74c3c; - padding: 0 10px; -} - -#chat .unread-marker:last-child { - display: none; -} - -.inline-channel { - cursor: pointer; -} - -.inline-channel:hover { - opacity: .6; -} - -#chat .time, -#chat .from, -#chat .text { - display: table-cell; - padding: 2px 0; - vertical-align: top; -} - -#chat .time { - color: #ddd; - text-align: right; - max-width: 46px; - min-width: 46px; -} - -#chat .from { - border-right: 1px solid #f6f6f6; - color: #b1c3ce; - padding-right: 10px; - text-align: right; - max-width: 134px; - min-width: 134px; -} - -#loading a, -#chat .special .time, -#chat .special .from { - display: none; -} - -#chat a { - color: #50a656; -} - -/* Nicknames */ - -#chat .user { - cursor: pointer; - color: #50a656; -} - -#chat .user:hover { - opacity: .6; -} - -#chat.colored-nicks .user.color-1 { color: #1396cf; } -#chat.colored-nicks .user.color-2 { color: #ffcf89; } -#chat.colored-nicks .user.color-3 { color: #00dc5f; } -#chat.colored-nicks .user.color-4 { color: #ff5bc8; } -#chat.colored-nicks .user.color-5 { color: #ff0e1b; } -#chat.colored-nicks .user.color-6 { color: #000094; } -#chat.colored-nicks .user.color-7 { color: #006441; } -#chat.colored-nicks .user.color-8 { color: #00566e; } -#chat.colored-nicks .user.color-9 { color: #ff0078; } -#chat.colored-nicks .user.color-10 { color: #15d5a3; } -#chat.colored-nicks .user.color-11 { color: #006b3b; } -#chat.colored-nicks .user.color-12 { color: #00c5ba; } -#chat.colored-nicks .user.color-13 { color: #00465b; } -#chat.colored-nicks .user.color-14 { color: #ffafce; } -#chat.colored-nicks .user.color-15 { color: #ff3b12; } -#chat.colored-nicks .user.color-16 { color: #16cc6a; } -#chat.colored-nicks .user.color-17 { color: #ff0072; } -#chat.colored-nicks .user.color-18 { color: #ff5877; } -#chat.colored-nicks .user.color-19 { color: #ff1753; } -#chat.colored-nicks .user.color-20 { color: #007a56; } -#chat.colored-nicks .user.color-21 { color: #095092; } -#chat.colored-nicks .user.color-22 { color: #000bde; } -#chat.colored-nicks .user.color-23 { color: #00bca9; } -#chat.colored-nicks .user.color-24 { color: #00367d; } -#chat.colored-nicks .user.color-25 { color: #009ec4; } -#chat.colored-nicks .user.color-26 { color: #006119; } -#chat.colored-nicks .user.color-27 { color: #008bb8; } -#chat.colored-nicks .user.color-28 { color: #469c00; } -#chat.colored-nicks .user.color-29 { color: #ff0f95; } -#chat.colored-nicks .user.color-30 { color: #ff7615; } -#chat.colored-nicks .user.color-31 { color: #ff4846; } -#chat.colored-nicks .user.color-32 { color: #ff199b; } - -#chat .text { - padding-left: 10px; - padding-right: 6px; -} - -#chat .self .text { - color: #999; -} - -#chat .msg.motd .text, -#chat .msg.message .text, -#chat .msg.action .action-text, -#chat .msg.notice .text { - white-space: pre-wrap; - overflow: hidden; -} - -#chat .msg.channel_list_loading .text { - color: #999; - font-style: italic; - padding-left: 20px; -} - -#chat .msg.channel_list_truncated .text { - color: #f00; - padding-left: 20px; -} - -#chat table.channel-list { - margin: 5px 10px; - width: calc(100% - 30px); -} - -#chat table.channel-list th, -#chat table.channel-list td { - padding: 5px; - vertical-align: top; - border-bottom: #eee 1px solid; -} - -#chat table.channel-list .channel, -#chat table.channel-list .topic { - text-align: left; -} - -#chat table.channel-list .users { - text-align: center; -} - -#chat table.channel-list td.channel .inline-channel { - color: #428bca; -} - -#chat table.channel-list td { - color: #555; -} - -#chat.hide-join .join, -#chat.hide-mode .mode, -#chat.hide-motd .motd, -#chat.hide-nick .nick, -#chat.hide-part .part, -#chat.hide-quit .quit { - display: none !important; -} - -#chat .join .text, -#chat .kick .text, -#chat .mode .text, -#chat .nick .text, -#chat .part .text, -#chat .quit .text, -#chat .topic .text, -#chat .topic_set_by .text { - color: #999; -} - -#chat .action .from, -#chat .action .text, -#chat .action .user { - color: #f39c12; -} - -#chat .notice .time, -#chat .notice .text, -#chat .chan .notice .user { - color: #0074d9 !important; -} - -#chat .notice .user:before { - content: "Notice: "; -} - -#chat .error, -#chat .error .from, -#chat .channel .highlight .from, -#chat .channel .highlight .text, -#chat .channel .highlight .user { - color: #f00; -} - -#chat .msg.toggle .time { - visibility: hidden; -} - -#chat .toggle-button { - background: #f5f5f5; - border-radius: 2px; - display: inline-block; - color: #666; - height: 1em; - line-height: 0; - padding: 0 6px; -} - -#chat .toggle-content { - background: #f5f5f5; - border-radius: 2px; - display: none; - color: #222; - font-size: 12px; - max-width: 100%; - padding: 6px 8px; - margin-top: 2px; -} - -#chat .toggle-content a { - color: inherit; -} - -#chat .toggle-content img { - max-width: 100%; - max-height: 250px; - display: block; - margin: 2px 0; -} - -#chat .toggle-content .thumb { - max-height: 110px; - max-width: 210px; -} - -#chat .toggle-content .head { - font-weight: bold; -} - -#chat .toggle-content .body { - color: #999; - max-width: 460px; - word-break: normal; - word-wrap: break-word; -} - -#chat .toggle-content.show { - display: inline-block !important; -} - -#chat .count { - background: #fafafa; - height: 48px; - left: 0; - position: absolute; - right: 0; - top: 0; -} - -#chat .search { - color: #222; - border: 0; - background: none; - font: inherit; - outline: 0; - padding: 18px 16px; - position: relative; - width: 100%; -} - -#chat .names { - bottom: 0; - overflow: auto; - overflow-x: hidden; - -webkit-overflow-scrolling: touch; - padding-bottom: 10px; - position: absolute; - top: 48px; - width: 100%; -} - -#chat .names .user { - display: block; - line-height: 1.6; - padding: 0 16px; -} - -#chat .user-mode:before { - content: ""; - border-bottom: 1px solid #eee; - display: block; - line-height: 1.6; - padding: 12px 16px 10px; - margin-bottom: 10px; -} - -#chat .user-mode.owner:before { - content: "Owners"; -} - -#chat .user-mode.admin:before { - content: "Administrators"; -} - -#chat .user-mode.op:before { - content: "Operators"; -} - -#chat .user-mode.half-op:before { - content: "Half-Operators"; -} - -#chat .user-mode.voice:before { - content: "Voiced"; -} - -#chat .user-mode.normal:before { - content: "Users"; -} - -#loading { - font-size: 14px; - z-index: 1; -} - -#loading p { - margin-top: 10px; -} - -#loading-slow { - display: none; -} - -#sign-in label { - display: block; - margin-top: 10px; -} - -#sign-in .remember { - float: left; - font-size: 14px; - margin-top: 12px; -} - -#sign-in .remember input { - float: left; - margin: 3px 10px 0 0; -} - -#sign-in .btn { - margin-top: 25px; -} - -#sign-in .error { - color: #e74c3c; - margin-top: 1em; -} - -#connect label { - display: block; - margin-top: 11px; -} - -#connect .port:before { - content: ":"; - margin: 9px 0 0 -17px; - position: absolute; -} - -#connect .tls { - float: left; - font-size: 14px; - margin-top: 6px; -} - -#connect .tls input { - float: left; - margin: 3px 10px 0 0; -} - -#connect .btn { - float: left; - margin-top: 30px; -} - -#settings .opt { - display: block; - padding: 5px 0 10px 1px; -} - -#settings .opt input { - float: left; - margin: 4px 10px 0 0; -} - -#settings .about, -#settings #play { - color: #7f8c8d; -} - -#settings .about small { - margin-left: 2px; -} - -#settings #play { - font-size: 14px; - transition: opacity .2s; -} - -#settings #play:hover { - opacity: .8; -} - -#settings .about { - font-size: 14px; - padding-top: 2px; - line-height: 1.8; -} - -#settings #change-password .error, -#settings #change-password .success { - margin-bottom: 1em; -} - -#settings #change-password .error { - color: #e74c3c; -} - -#settings #change-password .success { - color: #2ecc40; -} - -#settings .error { - color: #e74c3c; - margin-top: .2em; -} - -#form { - background: #eee; - border-top: 1px solid #ddd; - -webkit-flex: 0 0 auto; - flex: 0 0 auto; - padding: 5px; -} - -#windows #form .input { - font: 12px Consolas, Menlo, Monaco, "Lucida Console", "DejaVu Sans Mono", "Courier New", monospace; - border: 1px solid #ddd; - border-radius: 2px; - margin: 0; - padding: 0; - background: white; - display: -webkit-flex; - display: flex; - -webkit-align-items: flex-end; - align-items: flex-end; -} - -[contenteditable]:focus { - outline: none; -} - -/* Nick editor */ - -#form #nick { - background: #f6f6f6; - color: #666; - font: inherit; - font-size: 11px; - margin: 4px; - line-height: 22px; - height: 24px; - padding-left: 9px; - padding-right: 5px; - border-radius: 2px; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -webkit-flex: 0 0 auto; - flex: 0 0 auto; - border: 1px solid transparent; - transition: border-color .2s; -} - -#form #nick-value { - padding-right: 5px; -} - -#form #nick.editable { - border-color: black; -} - -#nick button#set-nick, -#nick button#submit-nick, -#nick button#cancel-nick { - color: #aaa; - width: 18px; -} - -#nick.editable button#set-nick, -#nick.editable #set-nick-tooltip, -#nick button#submit-nick, -#nick:not(.editable) #save-nick-tooltip, -#nick button#cancel-nick, -#nick:not(.editable) #cancel-nick-tooltip { - display: none; -} - -#nick.editable button#submit-nick, -#nick.editable button#cancel-nick { - display: inline-block; -} - -/* End nick editor */ - -#form #input { - background: transparent; - border: none; - font: inherit; - min-height: 18px; /* Required when computing input height at char deletion */ - height: 18px; - max-height: 90px; - line-height: 1.5; - outline: none; - margin: 5px; - padding: 0; - resize: none; - -webkit-flex: 1 0 auto; - flex: 1 0 auto; - align-self: center; -} - -#form #submit { - color: #9ca5b4; - font-size: 14px; - height: 32px; - transition: opacity .2s; - width: 32px; - -webkit-flex: 0 0 auto; - flex: 0 0 auto; -} - -#form #submit:hover { - opacity: .6; -} - -#context-menu-container { - display: none; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 1000; - background: transparent; -} - -#context-menu { - position: absolute; - list-style: none; - margin: 0; - padding: 0; - min-width: 160px; - font-size: 14px; - background-color: #fff; - box-shadow: 0 3px 12px rgba(0, 0, 0, .15); - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 2px; -} - -.context-menu-divider { - height: 1px; - margin: 6px 0; - background-color: rgba(0, 0, 0, .1); -} - -.context-menu-item { - cursor: pointer; - display: block; - padding: 4px 8px; - color: #333; - margin-top: 6px; - margin-bottom: 6px; -} - -.context-menu-item:hover { - background-color: #f6f6f6; -} - -.context-menu-item:before { - width: 20px; - display: inline-block; -} - -/** - * Tooltips - * See http://primercss.io/tooltips/ - */ -.tooltipped { - position: relative; -} - -.tooltipped:after { - position: absolute; - z-index: 1000000; - display: inline-block; - visibility: hidden; - opacity: 0; - padding: 5px 8px; - font: 12px Lato; - line-height: 1.2; - color: #fff; - text-align: center; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-wrap: break-word; - white-space: pre; - pointer-events: none; - content: attr(aria-label); - background: #222; - border-radius: 3px; - -webkit-font-smoothing: subpixel-antialiased; - transition: .2s; -} - -.tooltipped:before { - position: absolute; - z-index: 1000001; - display: inline-block; - visibility: hidden; - opacity: 0; - width: 0; - height: 0; - color: #fff; - pointer-events: none; - content: ""; - border: 5px solid transparent; - transition: .2s; -} - -.tooltipped:hover:before, -.tooltipped:hover:after, -.tooltipped:active:before, -.tooltipped:active:after, -.tooltipped:focus:before, -.tooltipped:focus:after { - visibility: visible; - opacity: 1; - text-decoration: none; -} - -.tooltipped-s:after, -.tooltipped-se:after, -.tooltipped-sw:after { - top: 100%; - right: 50%; - margin-top: 5px; -} - -.tooltipped-s:before, -.tooltipped-se:before, -.tooltipped-sw:before { - top: auto; - right: 50%; - bottom: -5px; - margin-right: -5px; - border-bottom-color: #222; -} - -.tooltipped-se:after { - right: auto; - left: 50%; - margin-left: -15px; -} - -.tooltipped-sw:after { - margin-right: -15px; -} - -.tooltipped-n:after, -.tooltipped-ne:after, -.tooltipped-nw:after { - right: 50%; - bottom: 100%; - margin-bottom: 5px; -} - -.tooltipped-n:before, -.tooltipped-ne:before, -.tooltipped-nw:before { - top: -5px; - right: 50%; - bottom: auto; - margin-right: -5px; - border-top-color: #222; -} - -.tooltipped-ne:after { - right: auto; - left: 50%; - margin-left: -15px; -} - -.tooltipped-nw:after { - margin-right: -15px; -} - -.tooltipped-s:after, -.tooltipped-n:after { - -webkit-transform: translateX(50%); - transform: translateX(50%); -} - -.tooltipped-w:after { - right: 100%; - bottom: 50%; - margin-right: 5px; - -webkit-transform: translateY(50%); - transform: translateY(50%); -} - -.tooltipped-w:before { - top: 50%; - bottom: 50%; - left: -5px; - margin-top: -5px; - border-left-color: #222; -} - -.tooltipped-e:after { - bottom: 50%; - left: 100%; - margin-left: 5px; - -webkit-transform: translateY(50%); - transform: translateY(50%); -} - -.tooltipped-e:before { - top: 50%; - right: -5px; - bottom: 50%; - margin-top: -5px; - border-right-color: #222; -} - -/* End tooltips */ - -/** - * IRC Message Styling - * https://github.com/megawac/irc-style-parser - * Colours are credit to http://clrs.cc/ - */ -.irc-fg0 { color: #fff; } - -.irc-fg1 { color: #000; } - -.irc-fg2 { color: #001f3f; } - -.irc-fg3 { color: #2ecc40; } - -.irc-fg4 { color: #ff4136; } - -.irc-fg5 { color: #85144b; } - -.irc-fg6 { color: #b10dc9; } - -.irc-fg7 { color: #ff851b; } - -.irc-fg8 { color: #ffdc00; } - -.irc-fg9 { color: #01ff70; } - -.irc-fg10 { color: #39cccc; } - -.irc-fg11 { color: #7fdbff; } - -.irc-fg12 { color: #0074d9; } - -.irc-fg13 { color: #f012be; } - -.irc-fg14 { color: #aaa; } - -.irc-fg15 { color: #ddd; } - -.irc-bg0 { background: #fff; } - -.irc-bg1 { background: #000; } - -.irc-bg2 { background: #001f3f; } - -.irc-bg3 { background: #2ecc40; } - -.irc-bg4 { background: #ff4136; } - -.irc-bg5 { background: #85144b; } - -.irc-bg6 { background: #b10dc9; } - -.irc-bg7 { background: #ff851b; } - -.irc-bg8 { background: #ffdc00; } - -.irc-bg9 { background: #01ff70; } - -.irc-bg10 { background: #39cccc; } - -.irc-bg11 { background: #7fdbff; } - -.irc-bg12 { background: #0074d9; } - -.irc-bg13 { background: #f012be; } - -.irc-bg14 { background: #aaa; } - -.irc-bg15 { background: #ddd; } - -.irc-bold { - font-weight: bold; -} - -.irc-underline { - text-decoration: underline; -} - -.irc-italic { - font-style: italic; -} - -@media (max-width: 768px) { - /** - * TODO Replace this with `@media (hover: hover)` when Firefox supports it - * See: - * - http://stackoverflow.com/a/28058919/1935861 - * - http://caniuse.com/#feat=css-media-interaction - * - https://www.w3.org/TR/mediaqueries-4/ - * - https://developer.mozilla.org/en-US/docs/Web/CSS/@media/hover - */ - .tooltipped:hover:before, - .tooltipped:hover:after, - .tooltipped:active:before, - .tooltipped:active:after, - .tooltipped:focus:before, - .tooltipped:focus:after { - visibility: hidden; - opacity: 0; - } - - .container { - margin-top: 60px !important; - } - - #viewport.rt #chat .sidebar { - -webkit-transform: translate3d(-180px, 0, 0); - transform: translate3d(-180px, 0, 0); - } - - #sidebar, - #footer { - left: -220px; - } - - #sidebar .empty:before { - margin-top: 0; - } - - #main { - left: 0; - } - - #chat .chat { - right: 0; - } - - #viewport .lt, - #viewport .channel .rt { - display: block; - } - - #windows .window .header { - display: block; - } - - #chat .sidebar { - right: -180px; - } - - #chat .title:before { - display: none; - } -} - -@media (min-width: 769px) { - #viewport { - -webkit-transform: none !important; - transform: none !important; - } - - #viewport.menu-open { - transition: none; - } -} - -@media (max-width: 479px) { - .container { - margin: 40px 0 !important; - } - - #connect .tls { - margin: 20px 0; - } - - #windows .input { - margin-bottom: 2px; - } - - #chat .messages { - display: block; - padding: 5px 10px; - } - - #chat .msg { - display: block; - padding: 2px 0; - } - - #chat .time, - #chat .from, - #chat .text { - border: 0; - display: inline; - padding: 0; - } - - #chat .unread-marker { - margin: 0; - } -} - -::-webkit-scrollbar { - width: 8px; - background-color: rgba(0, 0, 0, 0); -} - -::-webkit-scrollbar:hover { - background-color: rgba(0, 0, 0, .09); -} - -::-webkit-scrollbar-thumb:vertical { - background: rgba(0, 0, 0, .5); - border-radius: 100px; -} - -::-webkit-scrollbar-thumb:vertical:active { - background: rgba(0, 0, 0, .6); -} diff --git a/sources/client/img/apple-touch-icon-120x120.png b/sources/client/img/apple-touch-icon-120x120.png deleted file mode 100644 index 6d7bf41..0000000 Binary files a/sources/client/img/apple-touch-icon-120x120.png and /dev/null differ diff --git a/sources/client/img/favicon-notification.png b/sources/client/img/favicon-notification.png deleted file mode 100644 index 1eb1e77..0000000 Binary files a/sources/client/img/favicon-notification.png and /dev/null differ diff --git a/sources/client/img/favicon.png b/sources/client/img/favicon.png deleted file mode 100644 index 3dca950..0000000 Binary files a/sources/client/img/favicon.png and /dev/null differ diff --git a/sources/client/img/logo-64.png b/sources/client/img/logo-64.png deleted file mode 100644 index f7dd344..0000000 Binary files a/sources/client/img/logo-64.png and /dev/null differ diff --git a/sources/client/img/logo-dark.svg b/sources/client/img/logo-dark.svg deleted file mode 100644 index dd0d301..0000000 --- a/sources/client/img/logo-dark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/sources/client/img/logo.svg b/sources/client/img/logo.svg deleted file mode 100644 index 2378f20..0000000 --- a/sources/client/img/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/sources/client/img/touch-icon-192x192.png b/sources/client/img/touch-icon-192x192.png deleted file mode 100644 index 976f79c..0000000 Binary files a/sources/client/img/touch-icon-192x192.png and /dev/null differ diff --git a/sources/client/index.html b/sources/client/index.html deleted file mode 100644 index 25be00a..0000000 --- a/sources/client/index.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - - - - - - - - - - The Lounge - - - - - - - - - - - - "> - -
-
- -
- - - - -
-
-
-
-
-
-
-

The Lounge is loading…

-
-
-

Loading the app… Make sure to have JavaScript enabled.

-

This is taking longer than it should, there might be connectivity issues.

- -
-
-
-
-
-
-
-
- - - - - - - -
-
-
-
-
-
-
-

Sign in to The Lounge

-
-
- -
-
- -
-
- -
- -
- -
-
-
-
-
-
- -
-
-
-
-

- <%= public ? "The Lounge - " : "" %> - Connect - <%= !displayNetwork && lockNetwork ? "to " + defaults.name : "" %> -

-
-
> -
-

Network settings

-
-
- -
-
- -
-
- -
-
- > -
-
-
- > -
-
-
-
- -
-
- -
-
- -
-
-
-
-

User preferences

-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-

Settings

-
-
-

Messages

-
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-

Visual Aids

-
-
- -
-
-

Theme

-
-
- - -
- <% if (typeof prefetch === "undefined" || prefetch !== false) { %> -
-

Links and URLs

-
-
- -
-
- -
- <% } %> -
-

Notifications

-
-
- -
-
- -
-
-
- -
-
- -
- -
- -
- -
- - <% if (!public && !ldap.enable) { %> -
-
-
-

Change password

-
-
- - -
-
- - -
-
- - -
- -
- -
-
-
- <% } %> -
-

Custom Stylesheet

-
-
- -
-
-

About The Lounge

-
-
-

- <% if (gitCommit) { %> - The Lounge is running from source - (<%= gitCommit %>).
- <% } else { %> - The Lounge is in version <%= version %> - (See release notes).
- <% } %> - - Website
- Documentation
- Report a bug -

-
-
-
-
-
-
-
-
- -
-
    -
    - - - - - - - - diff --git a/sources/client/js/libs/handlebars.js b/sources/client/js/libs/handlebars.js deleted file mode 100644 index 8c495a5..0000000 --- a/sources/client/js/libs/handlebars.js +++ /dev/null @@ -1,1240 +0,0 @@ -/*! - - handlebars v4.0.5 - -Copyright (C) 2011-2015 by Yehuda Katz - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -@license -*/ -(function webpackUniversalModuleDefinition(root, factory) { - if(typeof exports === 'object' && typeof module === 'object') - module.exports = factory(); - else if(typeof define === 'function' && define.amd) - define([], factory); - else if(typeof exports === 'object') - exports["Handlebars"] = factory(); - else - root["Handlebars"] = factory(); -})(this, function() { -return /******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; - -/******/ // The require function -/******/ function __webpack_require__(moduleId) { - -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; - -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; - -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); - -/******/ // Flag the module as loaded -/******/ module.loaded = true; - -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } - - -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; - -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; - -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; - -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireWildcard = __webpack_require__(1)['default']; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - - var _handlebarsBase = __webpack_require__(3); - - var base = _interopRequireWildcard(_handlebarsBase); - - // Each of these augment the Handlebars object. No need to setup here. - // (This is done to easily share code between commonjs and browse envs) - - var _handlebarsSafeString = __webpack_require__(17); - - var _handlebarsSafeString2 = _interopRequireDefault(_handlebarsSafeString); - - var _handlebarsException = __webpack_require__(5); - - var _handlebarsException2 = _interopRequireDefault(_handlebarsException); - - var _handlebarsUtils = __webpack_require__(4); - - var Utils = _interopRequireWildcard(_handlebarsUtils); - - var _handlebarsRuntime = __webpack_require__(18); - - var runtime = _interopRequireWildcard(_handlebarsRuntime); - - var _handlebarsNoConflict = __webpack_require__(19); - - var _handlebarsNoConflict2 = _interopRequireDefault(_handlebarsNoConflict); - - // For compatibility and usage outside of module systems, make the Handlebars object a namespace - function create() { - var hb = new base.HandlebarsEnvironment(); - - Utils.extend(hb, base); - hb.SafeString = _handlebarsSafeString2['default']; - hb.Exception = _handlebarsException2['default']; - hb.Utils = Utils; - hb.escapeExpression = Utils.escapeExpression; - - hb.VM = runtime; - hb.template = function (spec) { - return runtime.template(spec, hb); - }; - - return hb; - } - - var inst = create(); - inst.create = create; - - _handlebarsNoConflict2['default'](inst); - - inst['default'] = inst; - - exports['default'] = inst; - module.exports = exports['default']; - -/***/ }, -/* 1 */ -/***/ function(module, exports) { - - "use strict"; - - exports["default"] = function (obj) { - if (obj && obj.__esModule) { - return obj; - } else { - var newObj = {}; - - if (obj != null) { - for (var key in obj) { - if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; - } - } - - newObj["default"] = obj; - return newObj; - } - }; - - exports.__esModule = true; - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - "use strict"; - - exports["default"] = function (obj) { - return obj && obj.__esModule ? obj : { - "default": obj - }; - }; - - exports.__esModule = true; - -/***/ }, -/* 3 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - exports.HandlebarsEnvironment = HandlebarsEnvironment; - - var _utils = __webpack_require__(4); - - var _exception = __webpack_require__(5); - - var _exception2 = _interopRequireDefault(_exception); - - var _helpers = __webpack_require__(6); - - var _decorators = __webpack_require__(14); - - var _logger = __webpack_require__(16); - - var _logger2 = _interopRequireDefault(_logger); - - var VERSION = '4.0.5'; - exports.VERSION = VERSION; - var COMPILER_REVISION = 7; - - exports.COMPILER_REVISION = COMPILER_REVISION; - var REVISION_CHANGES = { - 1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it - 2: '== 1.0.0-rc.3', - 3: '== 1.0.0-rc.4', - 4: '== 1.x.x', - 5: '== 2.0.0-alpha.x', - 6: '>= 2.0.0-beta.1', - 7: '>= 4.0.0' - }; - - exports.REVISION_CHANGES = REVISION_CHANGES; - var objectType = '[object Object]'; - - function HandlebarsEnvironment(helpers, partials, decorators) { - this.helpers = helpers || {}; - this.partials = partials || {}; - this.decorators = decorators || {}; - - _helpers.registerDefaultHelpers(this); - _decorators.registerDefaultDecorators(this); - } - - HandlebarsEnvironment.prototype = { - constructor: HandlebarsEnvironment, - - logger: _logger2['default'], - log: _logger2['default'].log, - - registerHelper: function registerHelper(name, fn) { - if (_utils.toString.call(name) === objectType) { - if (fn) { - throw new _exception2['default']('Arg not supported with multiple helpers'); - } - _utils.extend(this.helpers, name); - } else { - this.helpers[name] = fn; - } - }, - unregisterHelper: function unregisterHelper(name) { - delete this.helpers[name]; - }, - - registerPartial: function registerPartial(name, partial) { - if (_utils.toString.call(name) === objectType) { - _utils.extend(this.partials, name); - } else { - if (typeof partial === 'undefined') { - throw new _exception2['default']('Attempting to register a partial called "' + name + '" as undefined'); - } - this.partials[name] = partial; - } - }, - unregisterPartial: function unregisterPartial(name) { - delete this.partials[name]; - }, - - registerDecorator: function registerDecorator(name, fn) { - if (_utils.toString.call(name) === objectType) { - if (fn) { - throw new _exception2['default']('Arg not supported with multiple decorators'); - } - _utils.extend(this.decorators, name); - } else { - this.decorators[name] = fn; - } - }, - unregisterDecorator: function unregisterDecorator(name) { - delete this.decorators[name]; - } - }; - - var log = _logger2['default'].log; - - exports.log = log; - exports.createFrame = _utils.createFrame; - exports.logger = _logger2['default']; - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - exports.extend = extend; - exports.indexOf = indexOf; - exports.escapeExpression = escapeExpression; - exports.isEmpty = isEmpty; - exports.createFrame = createFrame; - exports.blockParams = blockParams; - exports.appendContextPath = appendContextPath; - var escape = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '`': '`', - '=': '=' - }; - - var badChars = /[&<>"'`=]/g, - possible = /[&<>"'`=]/; - - function escapeChar(chr) { - return escape[chr]; - } - - function extend(obj /* , ...source */) { - for (var i = 1; i < arguments.length; i++) { - for (var key in arguments[i]) { - if (Object.prototype.hasOwnProperty.call(arguments[i], key)) { - obj[key] = arguments[i][key]; - } - } - } - - return obj; - } - - var toString = Object.prototype.toString; - - exports.toString = toString; - // Sourced from lodash - // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt - /* eslint-disable func-style */ - var isFunction = function isFunction(value) { - return typeof value === 'function'; - }; - // fallback for older versions of Chrome and Safari - /* istanbul ignore next */ - if (isFunction(/x/)) { - exports.isFunction = isFunction = function (value) { - return typeof value === 'function' && toString.call(value) === '[object Function]'; - }; - } - exports.isFunction = isFunction; - - /* eslint-enable func-style */ - - /* istanbul ignore next */ - var isArray = Array.isArray || function (value) { - return value && typeof value === 'object' ? toString.call(value) === '[object Array]' : false; - }; - - exports.isArray = isArray; - // Older IE versions do not directly support indexOf so we must implement our own, sadly. - - function indexOf(array, value) { - for (var i = 0, len = array.length; i < len; i++) { - if (array[i] === value) { - return i; - } - } - return -1; - } - - function escapeExpression(string) { - if (typeof string !== 'string') { - // don't escape SafeStrings, since they're already safe - if (string && string.toHTML) { - return string.toHTML(); - } else if (string == null) { - return ''; - } else if (!string) { - return string + ''; - } - - // Force a string conversion as this will be done by the append regardless and - // the regex test will do this transparently behind the scenes, causing issues if - // an object's to string has escaped characters in it. - string = '' + string; - } - - if (!possible.test(string)) { - return string; - } - return string.replace(badChars, escapeChar); - } - - function isEmpty(value) { - if (!value && value !== 0) { - return true; - } else if (isArray(value) && value.length === 0) { - return true; - } else { - return false; - } - } - - function createFrame(object) { - var frame = extend({}, object); - frame._parent = object; - return frame; - } - - function blockParams(params, ids) { - params.path = ids; - return params; - } - - function appendContextPath(contextPath, id) { - return (contextPath ? contextPath + '.' : '') + id; - } - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - - var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - - function Exception(message, node) { - var loc = node && node.loc, - line = undefined, - column = undefined; - if (loc) { - line = loc.start.line; - column = loc.start.column; - - message += ' - ' + line + ':' + column; - } - - var tmp = Error.prototype.constructor.call(this, message); - - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } - - /* istanbul ignore else */ - if (Error.captureStackTrace) { - Error.captureStackTrace(this, Exception); - } - - if (loc) { - this.lineNumber = line; - this.column = column; - } - } - - Exception.prototype = new Error(); - - exports['default'] = Exception; - module.exports = exports['default']; - -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - exports.registerDefaultHelpers = registerDefaultHelpers; - - var _helpersBlockHelperMissing = __webpack_require__(7); - - var _helpersBlockHelperMissing2 = _interopRequireDefault(_helpersBlockHelperMissing); - - var _helpersEach = __webpack_require__(8); - - var _helpersEach2 = _interopRequireDefault(_helpersEach); - - var _helpersHelperMissing = __webpack_require__(9); - - var _helpersHelperMissing2 = _interopRequireDefault(_helpersHelperMissing); - - var _helpersIf = __webpack_require__(10); - - var _helpersIf2 = _interopRequireDefault(_helpersIf); - - var _helpersLog = __webpack_require__(11); - - var _helpersLog2 = _interopRequireDefault(_helpersLog); - - var _helpersLookup = __webpack_require__(12); - - var _helpersLookup2 = _interopRequireDefault(_helpersLookup); - - var _helpersWith = __webpack_require__(13); - - var _helpersWith2 = _interopRequireDefault(_helpersWith); - - function registerDefaultHelpers(instance) { - _helpersBlockHelperMissing2['default'](instance); - _helpersEach2['default'](instance); - _helpersHelperMissing2['default'](instance); - _helpersIf2['default'](instance); - _helpersLog2['default'](instance); - _helpersLookup2['default'](instance); - _helpersWith2['default'](instance); - } - -/***/ }, -/* 7 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - exports['default'] = function (instance) { - instance.registerHelper('blockHelperMissing', function (context, options) { - var inverse = options.inverse, - fn = options.fn; - - if (context === true) { - return fn(this); - } else if (context === false || context == null) { - return inverse(this); - } else if (_utils.isArray(context)) { - if (context.length > 0) { - if (options.ids) { - options.ids = [options.name]; - } - - return instance.helpers.each(context, options); - } else { - return inverse(this); - } - } else { - if (options.data && options.ids) { - var data = _utils.createFrame(options.data); - data.contextPath = _utils.appendContextPath(options.data.contextPath, options.name); - options = { data: data }; - } - - return fn(context, options); - } - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - var _exception = __webpack_require__(5); - - var _exception2 = _interopRequireDefault(_exception); - - exports['default'] = function (instance) { - instance.registerHelper('each', function (context, options) { - if (!options) { - throw new _exception2['default']('Must pass iterator to #each'); - } - - var fn = options.fn, - inverse = options.inverse, - i = 0, - ret = '', - data = undefined, - contextPath = undefined; - - if (options.data && options.ids) { - contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]) + '.'; - } - - if (_utils.isFunction(context)) { - context = context.call(this); - } - - if (options.data) { - data = _utils.createFrame(options.data); - } - - function execIteration(field, index, last) { - if (data) { - data.key = field; - data.index = index; - data.first = index === 0; - data.last = !!last; - - if (contextPath) { - data.contextPath = contextPath + field; - } - } - - ret = ret + fn(context[field], { - data: data, - blockParams: _utils.blockParams([context[field], field], [contextPath + field, null]) - }); - } - - if (context && typeof context === 'object') { - if (_utils.isArray(context)) { - for (var j = context.length; i < j; i++) { - if (i in context) { - execIteration(i, i, i === context.length - 1); - } - } - } else { - var priorKey = undefined; - - for (var key in context) { - if (context.hasOwnProperty(key)) { - // We're running the iterations one step out of sync so we can detect - // the last iteration without have to scan the object twice and create - // an itermediate keys array. - if (priorKey !== undefined) { - execIteration(priorKey, i - 1); - } - priorKey = key; - i++; - } - } - if (priorKey !== undefined) { - execIteration(priorKey, i - 1, true); - } - } - } - - if (i === 0) { - ret = inverse(this); - } - - return ret; - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - - var _exception = __webpack_require__(5); - - var _exception2 = _interopRequireDefault(_exception); - - exports['default'] = function (instance) { - instance.registerHelper('helperMissing', function () /* [args, ]options */{ - if (arguments.length === 1) { - // A missing field in a {{foo}} construct. - return undefined; - } else { - // Someone is actually trying to call something, blow up. - throw new _exception2['default']('Missing helper: "' + arguments[arguments.length - 1].name + '"'); - } - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - exports['default'] = function (instance) { - instance.registerHelper('if', function (conditional, options) { - if (_utils.isFunction(conditional)) { - conditional = conditional.call(this); - } - - // Default behavior is to render the positive path if the value is truthy and not empty. - // The `includeZero` option may be set to treat the condtional as purely not empty based on the - // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. - if (!options.hash.includeZero && !conditional || _utils.isEmpty(conditional)) { - return options.inverse(this); - } else { - return options.fn(this); - } - }); - - instance.registerHelper('unless', function (conditional, options) { - return instance.helpers['if'].call(this, conditional, { fn: options.inverse, inverse: options.fn, hash: options.hash }); - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 11 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - - exports['default'] = function (instance) { - instance.registerHelper('log', function () /* message, options */{ - var args = [undefined], - options = arguments[arguments.length - 1]; - for (var i = 0; i < arguments.length - 1; i++) { - args.push(arguments[i]); - } - - var level = 1; - if (options.hash.level != null) { - level = options.hash.level; - } else if (options.data && options.data.level != null) { - level = options.data.level; - } - args[0] = level; - - instance.log.apply(instance, args); - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 12 */ -/***/ function(module, exports) { - - 'use strict'; - - exports.__esModule = true; - - exports['default'] = function (instance) { - instance.registerHelper('lookup', function (obj, field) { - return obj && obj[field]; - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 13 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - exports['default'] = function (instance) { - instance.registerHelper('with', function (context, options) { - if (_utils.isFunction(context)) { - context = context.call(this); - } - - var fn = options.fn; - - if (!_utils.isEmpty(context)) { - var data = options.data; - if (options.data && options.ids) { - data = _utils.createFrame(options.data); - data.contextPath = _utils.appendContextPath(options.data.contextPath, options.ids[0]); - } - - return fn(context, { - data: data, - blockParams: _utils.blockParams([context], [data && data.contextPath]) - }); - } else { - return options.inverse(this); - } - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - exports.registerDefaultDecorators = registerDefaultDecorators; - - var _decoratorsInline = __webpack_require__(15); - - var _decoratorsInline2 = _interopRequireDefault(_decoratorsInline); - - function registerDefaultDecorators(instance) { - _decoratorsInline2['default'](instance); - } - -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - exports['default'] = function (instance) { - instance.registerDecorator('inline', function (fn, props, container, options) { - var ret = fn; - if (!props.partials) { - props.partials = {}; - ret = function (context, options) { - // Create a new partials stack frame prior to exec. - var original = container.partials; - container.partials = _utils.extend({}, original, props.partials); - var ret = fn(context, options); - container.partials = original; - return ret; - }; - } - - props.partials[options.args[0]] = options.fn; - - return ret; - }); - }; - - module.exports = exports['default']; - -/***/ }, -/* 16 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - exports.__esModule = true; - - var _utils = __webpack_require__(4); - - var logger = { - methodMap: ['debug', 'info', 'warn', 'error'], - level: 'info', - - // Maps a given level value to the `methodMap` indexes above. - lookupLevel: function lookupLevel(level) { - if (typeof level === 'string') { - var levelMap = _utils.indexOf(logger.methodMap, level.toLowerCase()); - if (levelMap >= 0) { - level = levelMap; - } else { - level = parseInt(level, 10); - } - } - - return level; - }, - - // Can be overridden in the host environment - log: function log(level) { - level = logger.lookupLevel(level); - - if (typeof console !== 'undefined' && logger.lookupLevel(logger.level) <= level) { - var method = logger.methodMap[level]; - if (!console[method]) { - // eslint-disable-line no-console - method = 'log'; - } - - for (var _len = arguments.length, message = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - message[_key - 1] = arguments[_key]; - } - - console[method].apply(console, message); // eslint-disable-line no-console - } - } - }; - - exports['default'] = logger; - module.exports = exports['default']; - -/***/ }, -/* 17 */ -/***/ function(module, exports) { - - // Build out our basic SafeString type - 'use strict'; - - exports.__esModule = true; - function SafeString(string) { - this.string = string; - } - - SafeString.prototype.toString = SafeString.prototype.toHTML = function () { - return '' + this.string; - }; - - exports['default'] = SafeString; - module.exports = exports['default']; - -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - var _interopRequireWildcard = __webpack_require__(1)['default']; - - var _interopRequireDefault = __webpack_require__(2)['default']; - - exports.__esModule = true; - exports.checkRevision = checkRevision; - exports.template = template; - exports.wrapProgram = wrapProgram; - exports.resolvePartial = resolvePartial; - exports.invokePartial = invokePartial; - exports.noop = noop; - - var _utils = __webpack_require__(4); - - var Utils = _interopRequireWildcard(_utils); - - var _exception = __webpack_require__(5); - - var _exception2 = _interopRequireDefault(_exception); - - var _base = __webpack_require__(3); - - function checkRevision(compilerInfo) { - var compilerRevision = compilerInfo && compilerInfo[0] || 1, - currentRevision = _base.COMPILER_REVISION; - - if (compilerRevision !== currentRevision) { - if (compilerRevision < currentRevision) { - var runtimeVersions = _base.REVISION_CHANGES[currentRevision], - compilerVersions = _base.REVISION_CHANGES[compilerRevision]; - throw new _exception2['default']('Template was precompiled with an older version of Handlebars than the current runtime. ' + 'Please update your precompiler to a newer version (' + runtimeVersions + ') or downgrade your runtime to an older version (' + compilerVersions + ').'); - } else { - // Use the embedded version info since the runtime doesn't know about this revision yet - throw new _exception2['default']('Template was precompiled with a newer version of Handlebars than the current runtime. ' + 'Please update your runtime to a newer version (' + compilerInfo[1] + ').'); - } - } - } - - function template(templateSpec, env) { - /* istanbul ignore next */ - if (!env) { - throw new _exception2['default']('No environment passed to template'); - } - if (!templateSpec || !templateSpec.main) { - throw new _exception2['default']('Unknown template object: ' + typeof templateSpec); - } - - templateSpec.main.decorator = templateSpec.main_d; - - // Note: Using env.VM references rather than local var references throughout this section to allow - // for external users to override these as psuedo-supported APIs. - env.VM.checkRevision(templateSpec.compiler); - - function invokePartialWrapper(partial, context, options) { - if (options.hash) { - context = Utils.extend({}, context, options.hash); - if (options.ids) { - options.ids[0] = true; - } - } - - partial = env.VM.resolvePartial.call(this, partial, context, options); - var result = env.VM.invokePartial.call(this, partial, context, options); - - if (result == null && env.compile) { - options.partials[options.name] = env.compile(partial, templateSpec.compilerOptions, env); - result = options.partials[options.name](context, options); - } - if (result != null) { - if (options.indent) { - var lines = result.split('\n'); - for (var i = 0, l = lines.length; i < l; i++) { - if (!lines[i] && i + 1 === l) { - break; - } - - lines[i] = options.indent + lines[i]; - } - result = lines.join('\n'); - } - return result; - } else { - throw new _exception2['default']('The partial ' + options.name + ' could not be compiled when running in runtime-only mode'); - } - } - - // Just add water - var container = { - strict: function strict(obj, name) { - if (!(name in obj)) { - throw new _exception2['default']('"' + name + '" not defined in ' + obj); - } - return obj[name]; - }, - lookup: function lookup(depths, name) { - var len = depths.length; - for (var i = 0; i < len; i++) { - if (depths[i] && depths[i][name] != null) { - return depths[i][name]; - } - } - }, - lambda: function lambda(current, context) { - return typeof current === 'function' ? current.call(context) : current; - }, - - escapeExpression: Utils.escapeExpression, - invokePartial: invokePartialWrapper, - - fn: function fn(i) { - var ret = templateSpec[i]; - ret.decorator = templateSpec[i + '_d']; - return ret; - }, - - programs: [], - program: function program(i, data, declaredBlockParams, blockParams, depths) { - var programWrapper = this.programs[i], - fn = this.fn(i); - if (data || depths || blockParams || declaredBlockParams) { - programWrapper = wrapProgram(this, i, fn, data, declaredBlockParams, blockParams, depths); - } else if (!programWrapper) { - programWrapper = this.programs[i] = wrapProgram(this, i, fn); - } - return programWrapper; - }, - - data: function data(value, depth) { - while (value && depth--) { - value = value._parent; - } - return value; - }, - merge: function merge(param, common) { - var obj = param || common; - - if (param && common && param !== common) { - obj = Utils.extend({}, common, param); - } - - return obj; - }, - - noop: env.VM.noop, - compilerInfo: templateSpec.compiler - }; - - function ret(context) { - var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - var data = options.data; - - ret._setup(options); - if (!options.partial && templateSpec.useData) { - data = initData(context, data); - } - var depths = undefined, - blockParams = templateSpec.useBlockParams ? [] : undefined; - if (templateSpec.useDepths) { - if (options.depths) { - depths = context !== options.depths[0] ? [context].concat(options.depths) : options.depths; - } else { - depths = [context]; - } - } - - function main(context /*, options*/) { - return '' + templateSpec.main(container, context, container.helpers, container.partials, data, blockParams, depths); - } - main = executeDecorators(templateSpec.main, main, container, options.depths || [], data, blockParams); - return main(context, options); - } - ret.isTop = true; - - ret._setup = function (options) { - if (!options.partial) { - container.helpers = container.merge(options.helpers, env.helpers); - - if (templateSpec.usePartial) { - container.partials = container.merge(options.partials, env.partials); - } - if (templateSpec.usePartial || templateSpec.useDecorators) { - container.decorators = container.merge(options.decorators, env.decorators); - } - } else { - container.helpers = options.helpers; - container.partials = options.partials; - container.decorators = options.decorators; - } - }; - - ret._child = function (i, data, blockParams, depths) { - if (templateSpec.useBlockParams && !blockParams) { - throw new _exception2['default']('must pass block params'); - } - if (templateSpec.useDepths && !depths) { - throw new _exception2['default']('must pass parent depths'); - } - - return wrapProgram(container, i, templateSpec[i], data, 0, blockParams, depths); - }; - return ret; - } - - function wrapProgram(container, i, fn, data, declaredBlockParams, blockParams, depths) { - function prog(context) { - var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - var currentDepths = depths; - if (depths && context !== depths[0]) { - currentDepths = [context].concat(depths); - } - - return fn(container, context, container.helpers, container.partials, options.data || data, blockParams && [options.blockParams].concat(blockParams), currentDepths); - } - - prog = executeDecorators(fn, prog, container, depths, data, blockParams); - - prog.program = i; - prog.depth = depths ? depths.length : 0; - prog.blockParams = declaredBlockParams || 0; - return prog; - } - - function resolvePartial(partial, context, options) { - if (!partial) { - if (options.name === '@partial-block') { - partial = options.data['partial-block']; - } else { - partial = options.partials[options.name]; - } - } else if (!partial.call && !options.name) { - // This is a dynamic partial that returned a string - options.name = partial; - partial = options.partials[partial]; - } - return partial; - } - - function invokePartial(partial, context, options) { - options.partial = true; - if (options.ids) { - options.data.contextPath = options.ids[0] || options.data.contextPath; - } - - var partialBlock = undefined; - if (options.fn && options.fn !== noop) { - options.data = _base.createFrame(options.data); - partialBlock = options.data['partial-block'] = options.fn; - - if (partialBlock.partials) { - options.partials = Utils.extend({}, options.partials, partialBlock.partials); - } - } - - if (partial === undefined && partialBlock) { - partial = partialBlock; - } - - if (partial === undefined) { - throw new _exception2['default']('The partial ' + options.name + ' could not be found'); - } else if (partial instanceof Function) { - return partial(context, options); - } - } - - function noop() { - return ''; - } - - function initData(context, data) { - if (!data || !('root' in data)) { - data = data ? _base.createFrame(data) : {}; - data.root = context; - } - return data; - } - - function executeDecorators(fn, prog, container, depths, data, blockParams) { - if (fn.decorator) { - var props = {}; - prog = fn.decorator(prog, props, container, depths && depths[0], data, blockParams, depths); - Utils.extend(prog, props); - } - return prog; - } - -/***/ }, -/* 19 */ -/***/ function(module, exports) { - - /* WEBPACK VAR INJECTION */(function(global) {/* global window */ - 'use strict'; - - exports.__esModule = true; - - exports['default'] = function (Handlebars) { - /* istanbul ignore next */ - var root = typeof global !== 'undefined' ? global : window, - $Handlebars = root.Handlebars; - /* istanbul ignore next */ - Handlebars.noConflict = function () { - if (root.Handlebars === Handlebars) { - root.Handlebars = $Handlebars; - } - return Handlebars; - }; - }; - - module.exports = exports['default']; - /* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }()))) - -/***/ } -/******/ ]) -}); -; diff --git a/sources/client/js/libs/handlebars/colorClass.js b/sources/client/js/libs/handlebars/colorClass.js deleted file mode 100644 index bd37191..0000000 --- a/sources/client/js/libs/handlebars/colorClass.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - // Generates a string from "color-1" to "color-32" based on an input string - "colorClass", function(str) { - var hash = 0; - for (var i = 0; i < str.length; i++) { - hash += str.charCodeAt(i); - } - - return "color-" + (1 + hash % 32); - } -); diff --git a/sources/client/js/libs/handlebars/diff.js b/sources/client/js/libs/handlebars/diff.js deleted file mode 100644 index 17f366a..0000000 --- a/sources/client/js/libs/handlebars/diff.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; - -var diff; - -Handlebars.registerHelper( - "diff", function(a, opt) { - if (a !== diff) { - diff = a; - return opt.fn(this); - } - - return opt.inverse(this); - } -); diff --git a/sources/client/js/libs/handlebars/equal.js b/sources/client/js/libs/handlebars/equal.js deleted file mode 100644 index 4d8f34e..0000000 --- a/sources/client/js/libs/handlebars/equal.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "equal", function(a, b, opt) { - a = a.toString(); - b = b.toString(); - if (a === b) { - return opt.fn(this); - } - - return opt.inverse(this); - } -); diff --git a/sources/client/js/libs/handlebars/localetime.js b/sources/client/js/libs/handlebars/localetime.js deleted file mode 100644 index c8dc097..0000000 --- a/sources/client/js/libs/handlebars/localetime.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; - -Handlebars.registerHelper("localetime", function(time) { - return new Date(time).toLocaleString(); -}); diff --git a/sources/client/js/libs/handlebars/modes.js b/sources/client/js/libs/handlebars/modes.js deleted file mode 100644 index bf97b03..0000000 --- a/sources/client/js/libs/handlebars/modes.js +++ /dev/null @@ -1,16 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "modes", function(mode) { - var modes = { - "~": "owner", - "&": "admin", - "!": "admin", - "@": "op", - "%": "half-op", - "+": "voice", - "": "normal" - }; - return modes[mode]; - } -); diff --git a/sources/client/js/libs/handlebars/parse.js b/sources/client/js/libs/handlebars/parse.js deleted file mode 100644 index 71a4582..0000000 --- a/sources/client/js/libs/handlebars/parse.js +++ /dev/null @@ -1,126 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "parse", function(text) { - text = Handlebars.Utils.escapeExpression(text); - text = colors(text); - text = channels(text); - text = uri(text); - return text; - } -); - -function uri(text) { - return window.URI.withinString(text, function(url) { - if (url.indexOf("javascript:") === 0) { - return url; - } - var split = url.split("<"); - url = "" + split[0] + ""; - if (split.length > 1) { - url += "<" + split.slice(1).join("<"); - } - return url; - }); -} - -/** - * Channels names are strings of length up to fifty (50) characters. - * The only restriction on a channel name is that it SHALL NOT contain - * any spaces (' '), a control G (^G or ASCII 7), a comma (','). - * Channel prefix '&' is handled as '&' because this parser is executed - * after entities in the message have been escaped. This prevents a couple of bugs. - */ -function channels(text) { - return text.replace( - /(^|\s|\x07|,)((?:#|&)[^\x07\s,]{1,49})/g, - '$1$2' - ); -} - -/** - * MIRC compliant colour and style parser - * Unfortuanately this is a non trivial operation - * See this branch for source and tests - * https://github.com/megawac/irc-style-parser/tree/shout - */ -var styleCheck_Re = /[\x00-\x1F]/, - back_re = /^([0-9]{1,2})(,([0-9]{1,2}))?/, - colourKey = "\x03", - // breaks all open styles ^O (\x0F) - styleBreak = "\x0F"; - - -function styleTemplate(settings) { - return "" + settings.text + ""; -} - -var styles = [ - ["normal", "\x00", ""], ["underline", "\x1F"], - ["bold", "\x02"], ["italic", "\x1D"] -].map(function(style) { - var escaped = encodeURI(style[1]).replace("%", "\\x"); - return { - name: style[0], - style: style[2] ? style[2] : "irc-" + style[0], - key: style[1], - keyregex: new RegExp(escaped + "(.*?)(" + escaped + "|$)") - }; -}); - -function colors(line) { - // http://www.mirc.com/colors.html - // http://www.aviran.org/stripremove-irc-client-control-characters/ - // https://github.com/perl6/mu/blob/master/examples/rules/Grammar-IRC.pm - // regexs are cruel to parse this thing - - // already done? - if (!styleCheck_Re.test(line)) { - return line; - } - - // split up by the irc style break character ^O - if (line.indexOf(styleBreak) >= 0) { - return line.split(styleBreak).map(colors).join(""); - } - - var result = line; - var parseArr = result.split(colourKey); - var text, match, colour, background = ""; - for (var i = 0; i < parseArr.length; i++) { - text = parseArr[i]; - match = text.match(back_re); - if (!match) { - // ^C (no colour) ending. Escape current colour and carry on - background = ""; - continue; - } - colour = "irc-fg" + +match[1]; - // set the background colour - if (match[3]) { - background = " irc-bg" + +match[3]; - } - // update the parsed text result - result = result.replace(colourKey + text, styleTemplate({ - style: colour + background, - text: text.slice(match[0].length) - })); - } - - // Matching styles (italics/bold/underline) - // if only colours were this easy... - styles.forEach(function(style) { - if (result.indexOf(style.key) < 0) { - return; - } - - result = result.replace(style.keyregex, function(matchedTrash, matchedText) { - return styleTemplate({ - style: style.style, - text: matchedText - }); - }); - }); - - return result; -} diff --git a/sources/client/js/libs/handlebars/roundBadgeNumber.js b/sources/client/js/libs/handlebars/roundBadgeNumber.js deleted file mode 100644 index 5982acd..0000000 --- a/sources/client/js/libs/handlebars/roundBadgeNumber.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "roundBadgeNumber", function(count) { - if (count < 1000) { - return count; - } - - return (count / 1000).toFixed(2).slice(0, -1) + "k"; - } -); diff --git a/sources/client/js/libs/handlebars/tojson.js b/sources/client/js/libs/handlebars/tojson.js deleted file mode 100644 index 7df32f7..0000000 --- a/sources/client/js/libs/handlebars/tojson.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "toJSON", function(context) { - return JSON.stringify(context); - } -); diff --git a/sources/client/js/libs/handlebars/tz.js b/sources/client/js/libs/handlebars/tz.js deleted file mode 100644 index 9b46be6..0000000 --- a/sources/client/js/libs/handlebars/tz.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "tz", function(time) { - time = new Date(time); - var h = time.getHours(); - var m = time.getMinutes(); - - if (h < 10) { - h = "0" + h; - } - - if (m < 10) { - m = "0" + m; - } - - return h + ":" + m; - } -); diff --git a/sources/client/js/libs/handlebars/users.js b/sources/client/js/libs/handlebars/users.js deleted file mode 100644 index 977fbf9..0000000 --- a/sources/client/js/libs/handlebars/users.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; - -Handlebars.registerHelper( - "users", function(count) { - return count + " " + (count === 1 ? "user" : "users"); - } -); diff --git a/sources/client/js/libs/jquery.js b/sources/client/js/libs/jquery.js deleted file mode 100644 index 9f7b3d3..0000000 --- a/sources/client/js/libs/jquery.js +++ /dev/null @@ -1,9190 +0,0 @@ -/*! - * jQuery JavaScript Library v2.1.1 - * http://jquery.com/ - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * - * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-05-01T17:11Z - */ - -(function( global, factory ) { - - if ( typeof module === "object" && typeof module.exports === "object" ) { - // For CommonJS and CommonJS-like environments where a proper window is present, - // execute the factory and get jQuery - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a jQuery-making factory as module.exports - // This accentuates the need for the creation of a real window - // e.g. var jQuery = require("jquery")(window); - // See ticket #14549 for more info - module.exports = global.document ? - factory( global, true ) : - function( w ) { - if ( !w.document ) { - throw new Error( "jQuery requires a window with a document" ); - } - return factory( w ); - }; - } else { - factory( global ); - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { - -// Can't do this because several apps including ASP.NET trace -// the stack via arguments.caller.callee and Firefox dies if -// you try to trace through "use strict" call chains. (#13335) -// Support: Firefox 18+ -// - -var arr = []; - -var slice = arr.slice; - -var concat = arr.concat; - -var push = arr.push; - -var indexOf = arr.indexOf; - -var class2type = {}; - -var toString = class2type.toString; - -var hasOwn = class2type.hasOwnProperty; - -var support = {}; - - - -var - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - version = "2.1.1", - - // Define a local copy of jQuery - jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - // Need init if jQuery is called (just allow error to be thrown if not included) - return new jQuery.fn.init( selector, context ); - }, - - // Support: Android<4.1 - // Make sure we trim BOM and NBSP - rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, - - // Matches dashed string for camelizing - rmsPrefix = /^-ms-/, - rdashAlpha = /-([\da-z])/gi, - - // Used by jQuery.camelCase as callback to replace() - fcamelCase = function( all, letter ) { - return letter.toUpperCase(); - }; - -jQuery.fn = jQuery.prototype = { - // The current version of jQuery being used - jquery: version, - - constructor: jQuery, - - // Start with an empty selector - selector: "", - - // The default length of a jQuery object is 0 - length: 0, - - toArray: function() { - return slice.call( this ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num != null ? - - // Return just the one element from the set - ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - - // Return all the elements in a clean array - slice.call( this ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems ) { - - // Build a new jQuery matched element set - var ret = jQuery.merge( this.constructor(), elems ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - ret.context = this.context; - - // Return the newly-formed element set - return ret; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ) ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - eq: function( i ) { - var len = this.length, - j = +i + ( i < 0 ? len : 0 ); - return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); - }, - - end: function() { - return this.prevObject || this.constructor(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: arr.sort, - splice: arr.splice -}; - -jQuery.extend = jQuery.fn.extend = function() { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - - // skip the boolean and the target - target = arguments[ i ] || {}; - i++; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( i === length ) { - target = this; - i--; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging plain objects or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { - if ( copyIsArray ) { - copyIsArray = false; - clone = src && jQuery.isArray(src) ? src : []; - - } else { - clone = src && jQuery.isPlainObject(src) ? src : {}; - } - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - // Unique for each copy of jQuery on the page - expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - - // Assume jQuery is ready without the ready module - isReady: true, - - error: function( msg ) { - throw new Error( msg ); - }, - - noop: function() {}, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return jQuery.type(obj) === "function"; - }, - - isArray: Array.isArray, - - isWindow: function( obj ) { - return obj != null && obj === obj.window; - }, - - isNumeric: function( obj ) { - // parseFloat NaNs numeric-cast false positives (null|true|false|"") - // ...but misinterprets leading-number strings, particularly hex literals ("0x...") - // subtraction forces infinities to NaN - return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; - }, - - isPlainObject: function( obj ) { - // Not plain objects: - // - Any object or value whose internal [[Class]] property is not "[object Object]" - // - DOM nodes - // - window - if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.constructor && - !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - - // If the function hasn't returned already, we're confident that - // |obj| is a plain object, created by {} or constructed with new Object - return true; - }, - - isEmptyObject: function( obj ) { - var name; - for ( name in obj ) { - return false; - } - return true; - }, - - type: function( obj ) { - if ( obj == null ) { - return obj + ""; - } - // Support: Android < 4.0, iOS < 6 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ toString.call(obj) ] || "object" : - typeof obj; - }, - - // Evaluates a script in a global context - globalEval: function( code ) { - var script, - indirect = eval; - - code = jQuery.trim( code ); - - if ( code ) { - // If the code includes a valid, prologue position - // strict mode pragma, execute code by injecting a - // script tag into the document. - if ( code.indexOf("use strict") === 1 ) { - script = document.createElement("script"); - script.text = code; - document.head.appendChild( script ).parentNode.removeChild( script ); - } else { - // Otherwise, avoid the DOM node creation, insertion - // and removal by using an indirect global eval - indirect( code ); - } - } - }, - - // Convert dashed to camelCase; used by the css and data modules - // Microsoft forgot to hump their vendor prefix (#9572) - camelCase: function( string ) { - return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - }, - - // args is for internal usage only - each: function( obj, callback, args ) { - var value, - i = 0, - length = obj.length, - isArray = isArraylike( obj ); - - if ( args ) { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.apply( obj[ i ], args ); - - if ( value === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } else { - for ( i in obj ) { - value = callback.call( obj[ i ], i, obj[ i ] ); - - if ( value === false ) { - break; - } - } - } - } - - return obj; - }, - - // Support: Android<4.1 - trim: function( text ) { - return text == null ? - "" : - ( text + "" ).replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( arr, results ) { - var ret = results || []; - - if ( arr != null ) { - if ( isArraylike( Object(arr) ) ) { - jQuery.merge( ret, - typeof arr === "string" ? - [ arr ] : arr - ); - } else { - push.call( ret, arr ); - } - } - - return ret; - }, - - inArray: function( elem, arr, i ) { - return arr == null ? -1 : indexOf.call( arr, elem, i ); - }, - - merge: function( first, second ) { - var len = +second.length, - j = 0, - i = first.length; - - for ( ; j < len; j++ ) { - first[ i++ ] = second[ j ]; - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, invert ) { - var callbackInverse, - matches = [], - i = 0, - length = elems.length, - callbackExpect = !invert; - - // Go through the array, only saving the items - // that pass the validator function - for ( ; i < length; i++ ) { - callbackInverse = !callback( elems[ i ], i ); - if ( callbackInverse !== callbackExpect ) { - matches.push( elems[ i ] ); - } - } - - return matches; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var value, - i = 0, - length = elems.length, - isArray = isArraylike( elems ), - ret = []; - - // Go through the array, translating each of the items to their new values - if ( isArray ) { - for ( ; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - - // Go through every key on the object, - } else { - for ( i in elems ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret.push( value ); - } - } - } - - // Flatten any nested arrays - return concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // Quick check to determine if target is callable, in the spec - // this throws a TypeError, but we will just return undefined. - if ( !jQuery.isFunction( fn ) ) { - return undefined; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - - now: Date.now, - - // jQuery.support is not used in Core but other projects attach their - // properties to it so it needs to exist. - support: support -}); - -// Populate the class2type map -jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { - class2type[ "[object " + name + "]" ] = name.toLowerCase(); -}); - -function isArraylike( obj ) { - var length = obj.length, - type = jQuery.type( obj ); - - if ( type === "function" || jQuery.isWindow( obj ) ) { - return false; - } - - if ( obj.nodeType === 1 && length ) { - return true; - } - - return type === "array" || length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj; -} -var Sizzle = -/*! - * Sizzle CSS Selector Engine v1.10.19 - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-04-18 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - tokenize, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - // Local document vars - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - // Instance-specific data - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - // General-purpose constants - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - // Instance methods - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - // Use a stripped-down indexOf if we can't use a native one - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - // Regular expressions - - // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // http://www.w3.org/TR/css3-syntax/#characters - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - // Loosely modeled on CSS identifier characters - // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors - // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier - identifier = characterEncoding.replace( "w", "w#" ), - - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + - // Operator (capture 2) - "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" - "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + - "*\\]", - - pseudos = ":(" + characterEncoding + ")(?:\\((" + - // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: - // 1. quoted (capture 3; capture 4 or capture 5) - "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + - // 2. simple (capture 6) - "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + - // 3. anything else (capture 2) - ".*" + - ")\\)|)", - - // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - // For use in libraries implementing .is() - // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - // Easily-parseable/retrievable ID or TAG or CLASS selectors - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - // NaN means non-codepoint - // Support: Firefox<24 - // Workaround erroneous numeric interpretation of +"0x" - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - // BMP codepoint - String.fromCharCode( high + 0x10000 ) : - // Supplemental Plane codepoint (surrogate pair) - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -// Optimize for push.apply( _, NodeList ) -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - // Support: Android<4.0 - // Detect silently failing push.apply - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - // Can't trust NodeList.length - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - // QSA vars - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - // Shortcuts - if ( (match = rquickExpr.exec( selector )) ) { - // Speed-up: Sizzle("#ID") - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document (jQuery #6963) - if ( elem && elem.parentNode ) { - // Handle the case where IE, Opera, and Webkit return items - // by name instead of ID - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - // Context is not a document - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - // Speed-up: Sizzle("TAG") - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - // Speed-up: Sizzle(".CLASS") - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - // QSA path - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - // qSA works strangely on Element-rooted queries - // We can work around this by specifying an extra ID on the root - // and working up from there (Thanks to Andrew Dupont for the technique) - // IE 8 doesn't work on object elements - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key + " " ) > Expr.cacheLength ) { - // Only keep the most recent entries - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - // Remove from its parent by default - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - // release memory in IE - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - // Match elements found at the specified indexes - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * Checks a node for validity as a Sizzle context - * @param {Element|Object=} context - * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value - */ -function testContext( context ) { - return context && typeof context.getElementsByTagName !== strundefined && context; -} - -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document - * @returns {Object} Returns the current document - */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; - - // If no document and documentElement is available, return - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - // Set our document - document = doc; - docElem = doc.documentElement; - - // Support tests - documentIsHTML = !isXML( doc ); - - // Support: IE>8 - // If iframe document is assigned to "document" variable and if iframe has been reloaded, - // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 - // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent !== parent.top ) { - // IE11 does not have attachEvent, so all must suffer - if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans) - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - // Check if getElementsByClassName can be trusted - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
    "; - - // Support: Safari<4 - // Catch class over-caching - div.firstChild.className = "i"; - // Support: Opera<10 - // Catch gEBCN failure to find non-leading classes - return div.getElementsByClassName("i").length === 2; - }); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - // ID find and filter - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - return m && m.parentNode ? [ m ] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - // Support: IE6/7 - // getElementById is not reliable as a find shortcut - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - // Tag - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - // Class - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - // QSA and matchesSelector support - - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See http://bugs.jquery.com/ticket/13378 - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert(function( div ) { - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; - - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( div.querySelectorAll("[msallowclip^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Opera 10-11 does not throw on post-comma invalid pseudos - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( div, "div" ); - - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully does not implement inclusive descendent - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { - - // Flag for duplicate removal - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - // Sort on method existence if only one input has compareDocumentPosition - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - // Calculate position if both inputs belong to the same document - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - // Otherwise we know they are disconnected - 1; - - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[i], bp[i] ) : - - // Otherwise nodes in our document sort first - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - // Make sure that attribute selectors are quoted - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - // IE 9's matchesSelector returns false on disconnected nodes - if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [ elem ] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - // Set document vars if needed - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - // Set document vars if needed - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - // Clear input after sorting to release objects - // See https://github.com/jquery/sizzle/pull/225 - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - // If no nodeType, this is expected to be an array - while ( (node = elem[i++]) ) { - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - // Do not include comment or processing instruction nodes - - return ret; -}; - -Expr = Sizzle.selectors = { - - // Can be adjusted by the user - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - // nth-* requires argument - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - // numeric x and y parameters for Expr.filter.CHILD - // remember that false/true cast respectively to 0/1 - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - // other types prohibit arguments - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[6] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - // Accept quoted arguments as-is - if ( match[3] ) { - match[2] = match[4] || match[5] || ""; - - // Strip excess characters from unquoted arguments - } else if ( unquoted && rpseudo.test( unquoted ) && - // Get excess from tokenize (recursively) - (excess = tokenize( unquoted, true )) && - // advance to the next closing parenthesis - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - // excess is a negative index - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - // Return only captures needed by the pseudo filter method (type and argument) - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - // Shortcut for :nth-*(n) - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - // :(first|last|only)-(child|of-type) - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - // Reverse direction for :only-* (if we haven't yet done so) - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - // non-xml :nth-child(...) stores cache data on `parent` - if ( forward && useCache ) { - // Seek `elem` from a previously-cached index - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - // Fallback to seeking `elem` from the start - (diff = nodeIndex = 0) || start.pop()) ) { - - // When found, cache indexes on `parent` and break - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - // Use previously-cached element index if available - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) - } else { - // Use the same loop as above to seek `elem` from the start - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - // Cache the index of each encountered element - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - // Incorporate the offset, then check against cycle size - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes - // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters - // Remember that setFilters inherits from pseudos - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - // The user may use createPseudo to indicate that - // arguments are needed to create the filter function - // just as Sizzle does - if ( fn[ expando ] ) { - return fn( argument ); - } - - // But maintain support for old signatures - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - // Potentially complex pseudos - "not": markFunction(function( selector ) { - // Trim the selector passed to compile - // to avoid treating leading and trailing - // spaces as combinators - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - // Match elements unmatched by `matcher` - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - // "Whether an element is represented by a :lang() selector - // is based solely on the element's language value - // being equal to the identifier C, - // or beginning with the identifier C immediately followed by "-". - // The matching of C against the element's language value is performed case-insensitively. - // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { - // lang value must be a valid identifier - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - // Miscellaneous - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - // Boolean properties - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - // Contents - "empty": function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), - // but not by others (comment: 8; processing instruction: 7; etc.) - // nodeType < 6 works because attributes (2) do not appear as children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - // Element/input types - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - // Support: IE<8 - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - // Position-in-collection - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -// Add button/input type pseudos -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -// Easy API for creating new setFilters -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - // Comma and first run - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - // Don't consume trailing commas as valid - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - // Combinators - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - // Cast descendant combinators to space - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - // Filters - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - // Return the length of the invalid excess - // if we're just parsing - // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - // Check against closest ancestor/preceding element - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - // Check against all ancestor/preceding elements - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - // Assign to newCache so results back-propagate to previous elements - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - // Reuse newcache so results back-propagate to previous elements - outerCache[ dir ] = newCache; - - // A match means we're done; a fail means we have to keep checking - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - // Get initial elements from seed or context - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - // Prefilter to get matcher input, preserving a map for seed-results synchronization - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - // ...intermediate processing is necessary - [] : - - // ...otherwise use results directly - results : - matcherIn; - - // Find primary matches - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - // Apply postFilter - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - // Un-match failing elements by moving them back to matcherIn - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - // Get the final matcherOut by condensing this intermediate into postFinder contexts - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - // Restore matcherIn since elem is not yet a final match - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - // Move matched elements from seed to results to keep them synchronized - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - // Add elements to results, through postFinder if defined - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - // The foundational matcher ensures that elements are reachable from top-level context(s) - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - // Return special upon seeing a positional matcher - if ( matcher[ expando ] ) { - // Find the next relative operator (if any) for proper handling - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context !== document && context; - } - - // Add elements passing elementMatchers directly to results - // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - // Track unmatched elements for set filters - if ( bySet ) { - // They will have gone through all possible matchers - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - // Lengthen the array for every element, matched or not - if ( seed ) { - unmatched.push( elem ); - } - } - } - - // Apply set filters to unmatched elements - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - // Reintegrate element matches to eliminate the need for sorting - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - // Discard index placeholder values to get only actual matches - setMatched = condense( setMatched ); - } - - // Add matches to results - push.apply( results, setMatched ); - - // Seedless set matches succeeding multiple successful matchers stipulate sorting - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - // Override manipulation of globals by nested matchers - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - // Generate a function of recursive functions that can be used to check each element - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - // Cache the compiled function - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - // Save selector and tokenization - cached.selector = selector; - } - return cached; -}; - -/** - * A low-level selection function that works with Sizzle's compiled - * selector functions - * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile - * @param {Element} context - * @param {Array} [results] - * @param {Array} [seed] A set of elements to match against - */ -select = Sizzle.select = function( selector, context, results, seed ) { - var i, tokens, token, type, find, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - // Try to minimize operations if there is no seed and only one group - if ( match.length === 1 ) { - - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { - - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - - // Precompiled matchers will still verify ancestry, so step up a level - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - // Compile and execute a filtering function if one is not provided - // Provide `match` to avoid retokenization if we modified the selector above - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - -// One-time assignments - -// Sort stability -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -// Support: Chrome<14 -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - -// Initialize against the default document -setDocument(); - -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) -// Detached nodes confoundingly follow *each other* -support.sortDetached = assert(function( div1 ) { - // Should return 1, but returns 4 (following) - return div1.compareDocumentPosition( document.createElement("div") ) & 1; -}); - -// Support: IE<8 -// Prevent attribute/property "interpolation" -// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -return Sizzle; - -})( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.pseudos; -jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; - - - -var rneedsContext = jQuery.expr.match.needsContext; - -var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); - - - -var risSimple = /^.[^:#\[\.,]*$/; - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( risSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; - }); -} - -jQuery.filter = function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var i, - len = this.length, - ret = [], - self = this; - - if ( typeof selector !== "string" ) { - return this.pushStack( jQuery( selector ).filter(function() { - for ( i = 0; i < len; i++ ) { - if ( jQuery.contains( self[ i ], this ) ) { - return true; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - } -}); - - -// Initialize a jQuery object - - -// A central reference to the root jQuery(document) -var rootjQuery, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - init = jQuery.fn.init = function( selector, context ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - // Intentionally let the error be thrown if parseHTML is not present - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return typeof rootjQuery.ready !== "undefined" ? - rootjQuery.ready( selector ) : - // Execute immediately if ready is not present - selector( jQuery ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }; - -// Give the init function the jQuery prototype for later instantiation -init.prototype = jQuery.fn; - -// Initialize central reference -rootjQuery = jQuery( document ); - - -var rparentsprev = /^(?:parents|prev(?:Until|All))/, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; - -jQuery.extend({ - dir: function( elem, dir, until ) { - var matched = [], - truncate = until !== undefined; - - while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { - if ( elem.nodeType === 1 ) { - if ( truncate && jQuery( elem ).is( until ) ) { - break; - } - matched.push( elem ); - } - } - return matched; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -jQuery.fn.extend({ - has: function( target ) { - var targets = jQuery( target, this ), - l = targets.length; - - return this.filter(function() { - var i = 0; - for ( ; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - return this.pushStack( - jQuery.unique( - jQuery.merge( this.get(), jQuery( selector, context ) ) - ) - ); - }, - - addBack: function( selector ) { - return this.add( selector == null ? - this.prevObject : this.prevObject.filter(selector) - ); - } -}); - -function sibling( cur, dir ) { - while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} - return cur; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return elem.contentDocument || jQuery.merge( [], elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var matched = jQuery.map( this, fn, until ); - - if ( name.slice( -5 ) !== "Until" ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - matched = jQuery.filter( selector, matched ); - } - - if ( this.length > 1 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); -var rnotwhite = (/\S+/g); - - - -// String to Object options format cache -var optionsCache = {}; - -// Convert String-formatted options into Object-formatted ones and store in cache -function createOptions( options ) { - var object = optionsCache[ options ] = {}; - jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { - object[ flag ] = true; - }); - return object; -} - -/* - * Create a callback list using the following parameters: - * - * options: an optional list of space-separated options that will change how - * the callback list behaves or a more traditional option object - * - * By default a callback list will act like an event callback list and can be - * "fired" multiple times. - * - * Possible options: - * - * once: will ensure the callback list can only be fired once (like a Deferred) - * - * memory: will keep track of previous values and will call any callback added - * after the list has been fired right away with the latest "memorized" - * values (like a Deferred) - * - * unique: will ensure a callback can only be added once (no duplicate in the list) - * - * stopOnFalse: interrupt callings when a callback returns false - * - */ -jQuery.Callbacks = function( options ) { - - // Convert options from String-formatted to Object-formatted if needed - // (we check in cache first) - options = typeof options === "string" ? - ( optionsCache[ options ] || createOptions( options ) ) : - jQuery.extend( {}, options ); - - var // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list was already fired - fired, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = !options.once && [], - // Fire callbacks - fire = function( data ) { - memory = options.memory && data; - fired = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - firing = true; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { - memory = false; // To prevent further calls using add - break; - } - } - firing = false; - if ( list ) { - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - } else if ( memory ) { - list = []; - } else { - self.disable(); - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - // First, we save the current length - var start = list.length; - (function add( args ) { - jQuery.each( args, function( _, arg ) { - var type = jQuery.type( arg ); - if ( type === "function" ) { - if ( !options.unique || !self.has( arg ) ) { - list.push( arg ); - } - } else if ( arg && arg.length && type !== "string" ) { - // Inspect recursively - add( arg ); - } - }); - })( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away - } else if ( memory ) { - firingStart = start; - fire( memory ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - jQuery.each( arguments, function( _, arg ) { - var index; - while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { - list.splice( index, 1 ); - // Handle firing indexes - if ( firing ) { - if ( index <= firingLength ) { - firingLength--; - } - if ( index <= firingIndex ) { - firingIndex--; - } - } - } - }); - } - return this; - }, - // Check if a given callback is in the list. - // If no argument is given, return whether or not list has callbacks attached. - has: function( fn ) { - return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); - }, - // Remove all callbacks from the list - empty: function() { - list = []; - firingLength = 0; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( list && ( !fired || stack ) ) { - args = args || []; - args = [ context, args.slice ? args.slice() : args ]; - if ( firing ) { - stack.push( args ); - } else { - fire( args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!fired; - } - }; - - return self; -}; - - -jQuery.extend({ - - Deferred: function( func ) { - var tuples = [ - // action, add listener, listener list, final state - [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], - [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], - [ "notify", "progress", jQuery.Callbacks("memory") ] - ], - state = "pending", - promise = { - state: function() { - return state; - }, - always: function() { - deferred.done( arguments ).fail( arguments ); - return this; - }, - then: function( /* fnDone, fnFail, fnProgress */ ) { - var fns = arguments; - return jQuery.Deferred(function( newDefer ) { - jQuery.each( tuples, function( i, tuple ) { - var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; - // deferred[ done | fail | progress ] for forwarding actions to newDefer - deferred[ tuple[1] ](function() { - var returned = fn && fn.apply( this, arguments ); - if ( returned && jQuery.isFunction( returned.promise ) ) { - returned.promise() - .done( newDefer.resolve ) - .fail( newDefer.reject ) - .progress( newDefer.notify ); - } else { - newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); - } - }); - }); - fns = null; - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - return obj != null ? jQuery.extend( obj, promise ) : promise; - } - }, - deferred = {}; - - // Keep pipe for back-compat - promise.pipe = promise.then; - - // Add list-specific methods - jQuery.each( tuples, function( i, tuple ) { - var list = tuple[ 2 ], - stateString = tuple[ 3 ]; - - // promise[ done | fail | progress ] = list.add - promise[ tuple[1] ] = list.add; - - // Handle state - if ( stateString ) { - list.add(function() { - // state = [ resolved | rejected ] - state = stateString; - - // [ reject_list | resolve_list ].disable; progress_list.lock - }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); - } - - // deferred[ resolve | reject | notify ] - deferred[ tuple[0] ] = function() { - deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); - return this; - }; - deferred[ tuple[0] + "With" ] = list.fireWith; - }); - - // Make the deferred a promise - promise.promise( deferred ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }, - - // Deferred helper - when: function( subordinate /* , ..., subordinateN */ ) { - var i = 0, - resolveValues = slice.call( arguments ), - length = resolveValues.length, - - // the count of uncompleted subordinates - remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, - - // the master Deferred. If resolveValues consist of only a single Deferred, just use that. - deferred = remaining === 1 ? subordinate : jQuery.Deferred(), - - // Update function for both resolve and progress values - updateFunc = function( i, contexts, values ) { - return function( value ) { - contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; - if ( values === progressValues ) { - deferred.notifyWith( contexts, values ); - } else if ( !( --remaining ) ) { - deferred.resolveWith( contexts, values ); - } - }; - }, - - progressValues, progressContexts, resolveContexts; - - // add listeners to Deferred subordinates; treat others as resolved - if ( length > 1 ) { - progressValues = new Array( length ); - progressContexts = new Array( length ); - resolveContexts = new Array( length ); - for ( ; i < length; i++ ) { - if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { - resolveValues[ i ].promise() - .done( updateFunc( i, resolveContexts, resolveValues ) ) - .fail( deferred.reject ) - .progress( updateFunc( i, progressContexts, progressValues ) ); - } else { - --remaining; - } - } - } - - // if we're not waiting on anything, resolve the master - if ( !remaining ) { - deferred.resolveWith( resolveContexts, resolveValues ); - } - - return deferred.promise(); - } -}); - - -// The deferred used on DOM ready -var readyList; - -jQuery.fn.ready = function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; -}; - -jQuery.extend({ - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - jQuery( document ).off( "ready" ); - } - } -}); - -/** - * The ready event handler and self cleanup method - */ -function completed() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); -} - -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - -// Kick off the DOM ready check even if the user does not -jQuery.ready.promise(); - - - - -// Multifunctional method to get and set values of a collection -// The value/s can optionally be executed if it's a function -var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - len = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < len; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - len ? fn( elems[0], key ) : emptyGet; -}; - - -/** - * Determines whether an object can have data - */ -jQuery.acceptData = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - /* jshint -W018 */ - return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); -}; - - -function Data() { - // Support: Android < 4, - // Old WebKit does not have Object.preventExtensions/freeze method, - // return new empty object instead with no [[set]] accessor - Object.defineProperty( this.cache = {}, 0, { - get: function() { - return {}; - } - }); - - this.expando = jQuery.expando + Math.random(); -} - -Data.uid = 1; -Data.accepts = jQuery.acceptData; - -Data.prototype = { - key: function( owner ) { - // We can accept data for non-element nodes in modern browsers, - // but we should not, see #8335. - // Always return the key for a frozen object. - if ( !Data.accepts( owner ) ) { - return 0; - } - - var descriptor = {}, - // Check if the owner object already has a cache key - unlock = owner[ this.expando ]; - - // If not, create one - if ( !unlock ) { - unlock = Data.uid++; - - // Secure it in a non-enumerable, non-writable property - try { - descriptor[ this.expando ] = { value: unlock }; - Object.defineProperties( owner, descriptor ); - - // Support: Android < 4 - // Fallback to a less secure definition - } catch ( e ) { - descriptor[ this.expando ] = unlock; - jQuery.extend( owner, descriptor ); - } - } - - // Ensure the cache object - if ( !this.cache[ unlock ] ) { - this.cache[ unlock ] = {}; - } - - return unlock; - }, - set: function( owner, data, value ) { - var prop, - // There may be an unlock assigned to this node, - // if there is no entry for this "owner", create one inline - // and set the unlock as though an owner entry had always existed - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - // Handle: [ owner, key, value ] args - if ( typeof data === "string" ) { - cache[ data ] = value; - - // Handle: [ owner, { properties } ] args - } else { - // Fresh assignments by object are shallow copied - if ( jQuery.isEmptyObject( cache ) ) { - jQuery.extend( this.cache[ unlock ], data ); - // Otherwise, copy the properties one-by-one to the cache object - } else { - for ( prop in data ) { - cache[ prop ] = data[ prop ]; - } - } - } - return cache; - }, - get: function( owner, key ) { - // Either a valid cache is found, or will be created. - // New caches will be created and the unlock returned, - // allowing direct access to the newly created - // empty data object. A valid owner object must be provided. - var cache = this.cache[ this.key( owner ) ]; - - return key === undefined ? - cache : cache[ key ]; - }, - access: function( owner, key, value ) { - var stored; - // In cases where either: - // - // 1. No key was specified - // 2. A string key was specified, but no value provided - // - // Take the "read" path and allow the get method to determine - // which value to return, respectively either: - // - // 1. The entire cache object - // 2. The data stored at the key - // - if ( key === undefined || - ((key && typeof key === "string") && value === undefined) ) { - - stored = this.get( owner, key ); - - return stored !== undefined ? - stored : this.get( owner, jQuery.camelCase(key) ); - } - - // [*]When the key is not a string, or both a key and value - // are specified, set or extend (existing objects) with either: - // - // 1. An object of properties - // 2. A key and value - // - this.set( owner, key, value ); - - // Since the "set" path can have two possible entry points - // return the expected data based on which path was taken[*] - return value !== undefined ? value : key; - }, - remove: function( owner, key ) { - var i, name, camel, - unlock = this.key( owner ), - cache = this.cache[ unlock ]; - - if ( key === undefined ) { - this.cache[ unlock ] = {}; - - } else { - // Support array or space separated string of keys - if ( jQuery.isArray( key ) ) { - // If "name" is an array of keys... - // When data is initially created, via ("key", "val") signature, - // keys will be converted to camelCase. - // Since there is no way to tell _how_ a key was added, remove - // both plain key and camelCase key. #12786 - // This will only penalize the array argument path. - name = key.concat( key.map( jQuery.camelCase ) ); - } else { - camel = jQuery.camelCase( key ); - // Try the string as a key before any manipulation - if ( key in cache ) { - name = [ key, camel ]; - } else { - // If a key with the spaces exists, use it. - // Otherwise, create an array by matching non-whitespace - name = camel; - name = name in cache ? - [ name ] : ( name.match( rnotwhite ) || [] ); - } - } - - i = name.length; - while ( i-- ) { - delete cache[ name[ i ] ]; - } - } - }, - hasData: function( owner ) { - return !jQuery.isEmptyObject( - this.cache[ owner[ this.expando ] ] || {} - ); - }, - discard: function( owner ) { - if ( owner[ this.expando ] ) { - delete this.cache[ owner[ this.expando ] ]; - } - } -}; -var data_priv = new Data(); - -var data_user = new Data(); - - - -/* - Implementation Summary - - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, - rmultiDash = /([A-Z])/g; - -function dataAttr( elem, key, data ) { - var name; - - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? jQuery.parseJSON( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} - -jQuery.extend({ - hasData: function( elem ) { - return data_user.hasData( elem ) || data_priv.hasData( elem ); - }, - - data: function( elem, name, data ) { - return data_user.access( elem, name, data ); - }, - - removeData: function( elem, name ) { - data_user.remove( elem, name ); - }, - - // TODO: Now that all calls to _data and _removeData have been replaced - // with direct calls to data_priv methods, these can be deprecated. - _data: function( elem, name, data ) { - return data_priv.access( elem, name, data ); - }, - - _removeData: function( elem, name ) { - data_priv.remove( elem, name ); - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - var i, name, data, - elem = this[ 0 ], - attrs = elem && elem.attributes; - - // Gets all values - if ( key === undefined ) { - if ( this.length ) { - data = data_user.get( elem ); - - if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - i = attrs.length; - while ( i-- ) { - - // Support: IE11+ - // The attrs elements can be null (#14894) - if ( attrs[ i ] ) { - name = attrs[ i ].name; - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); - } - } - } - data_priv.set( elem, "hasDataAttrs", true ); - } - } - - return data; - } - - // Sets multiple values - if ( typeof key === "object" ) { - return this.each(function() { - data_user.set( this, key ); - }); - } - - return access( this, function( value ) { - var data, - camelKey = jQuery.camelCase( key ); - - // The calling jQuery object (element matches) is not empty - // (and therefore has an element appears at this[ 0 ]) and the - // `value` parameter was not undefined. An empty jQuery object - // will result in `undefined` for elem = this[ 0 ] which will - // throw an exception if an attempt to read a data cache is made. - if ( elem && value === undefined ) { - // Attempt to get data from the cache - // with the key as-is - data = data_user.get( elem, key ); - if ( data !== undefined ) { - return data; - } - - // Attempt to get data from the cache - // with the key camelized - data = data_user.get( elem, camelKey ); - if ( data !== undefined ) { - return data; - } - - // Attempt to "discover" the data in - // HTML5 custom data-* attrs - data = dataAttr( elem, camelKey, undefined ); - if ( data !== undefined ) { - return data; - } - - // We tried really hard, but the data doesn't exist. - return; - } - - // Set the data... - this.each(function() { - // First, attempt to store a copy or reference of any - // data that might've been store with a camelCased key. - var data = data_user.get( this, camelKey ); - - // For HTML5 data-* attribute interop, we have to - // store property names with dashes in a camelCase form. - // This might not apply to all properties...* - data_user.set( this, camelKey, value ); - - // *... In the case of properties that might _actually_ - // have dashes, we need to also store a copy of that - // unchanged property. - if ( key.indexOf("-") !== -1 && data !== undefined ) { - data_user.set( this, key, value ); - } - }); - }, null, value, arguments.length > 1, null, true ); - }, - - removeData: function( key ) { - return this.each(function() { - data_user.remove( this, key ); - }); - } -}); - - -jQuery.extend({ - queue: function( elem, type, data ) { - var queue; - - if ( elem ) { - type = ( type || "fx" ) + "queue"; - queue = data_priv.get( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( data ) { - if ( !queue || jQuery.isArray( data ) ) { - queue = data_priv.access( elem, type, jQuery.makeArray(data) ); - } else { - queue.push( data ); - } - } - return queue || []; - } - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), - startLength = queue.length, - fn = queue.shift(), - hooks = jQuery._queueHooks( elem, type ), - next = function() { - jQuery.dequeue( elem, type ); - }; - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - startLength--; - } - - if ( fn ) { - - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift( "inprogress" ); - } - - // clear up the last queue stop function - delete hooks.stop; - fn.call( elem, next, hooks ); - } - - if ( !startLength && hooks ) { - hooks.empty.fire(); - } - }, - - // not intended for public consumption - generates a queueHooks object, or returns the current one - _queueHooks: function( elem, type ) { - var key = type + "queueHooks"; - return data_priv.get( elem, key ) || data_priv.access( elem, key, { - empty: jQuery.Callbacks("once memory").add(function() { - data_priv.remove( elem, [ type + "queue", key ] ); - }) - }); - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - var setter = 2; - - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - setter--; - } - - if ( arguments.length < setter ) { - return jQuery.queue( this[0], type ); - } - - return data === undefined ? - this : - this.each(function() { - var queue = jQuery.queue( this, type, data ); - - // ensure a hooks for this queue - jQuery._queueHooks( this, type ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - }, - // Get a promise resolved when queues of a certain type - // are emptied (fx is the type by default) - promise: function( type, obj ) { - var tmp, - count = 1, - defer = jQuery.Deferred(), - elements = this, - i = this.length, - resolve = function() { - if ( !( --count ) ) { - defer.resolveWith( elements, [ elements ] ); - } - }; - - if ( typeof type !== "string" ) { - obj = type; - type = undefined; - } - type = type || "fx"; - - while ( i-- ) { - tmp = data_priv.get( elements[ i ], type + "queueHooks" ); - if ( tmp && tmp.empty ) { - count++; - tmp.empty.add( resolve ); - } - } - resolve(); - return defer.promise( obj ); - } -}); -var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; - -var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - -var isHidden = function( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); - }; - -var rcheckableType = (/^(?:checkbox|radio)$/i); - - - -(function() { - var fragment = document.createDocumentFragment(), - div = fragment.appendChild( document.createElement( "div" ) ), - input = document.createElement( "input" ); - - // #11217 - WebKit loses check when the name is after the checked attribute - // Support: Windows Web Apps (WWA) - // `name` and `type` need .setAttribute for WWA - input.setAttribute( "type", "radio" ); - input.setAttribute( "checked", "checked" ); - input.setAttribute( "name", "t" ); - - div.appendChild( input ); - - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Make sure textarea (and checkbox) defaultValue is properly cloned - // Support: IE9-IE11+ - div.innerHTML = ""; - support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; -})(); -var strundefined = typeof undefined; - - - -support.focusinBubbles = "onfocusin" in window; - - -var - rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, - rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, - rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; - -function returnTrue() { - return true; -} - -function returnFalse() { - return false; -} - -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - -/* - * Helper functions for managing events -- not part of the public interface. - * Props to Dean Edwards' addEvent library for many of the ideas. - */ -jQuery.event = { - - global: {}, - - add: function( elem, types, handler, data, selector ) { - - var handleObjIn, eventHandle, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.get( elem ); - - // Don't attach events to noData or text/comment nodes (but allow plain objects) - if ( !elemData ) { - return; - } - - // Caller can pass in an object of custom data in lieu of the handler - if ( handler.handler ) { - handleObjIn = handler; - handler = handleObjIn.handler; - selector = handleObjIn.selector; - } - - // Make sure that the handler has a unique ID, used to find/remove it later - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // Init the element's event structure and main handler, if this is the first - if ( !(events = elemData.events) ) { - events = elemData.events = {}; - } - if ( !(eventHandle = elemData.handle) ) { - eventHandle = elemData.handle = function( e ) { - // Discard the second event of a jQuery.event.trigger() and - // when an event is called after a page has unloaded - return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? - jQuery.event.dispatch.apply( elem, arguments ) : undefined; - }; - } - - // Handle multiple events separated by a space - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // There *must* be a type, no attaching namespace-only handlers - if ( !type ) { - continue; - } - - // If event changes its type, use the special event handlers for the changed type - special = jQuery.event.special[ type ] || {}; - - // If selector defined, determine special event api type, otherwise given type - type = ( selector ? special.delegateType : special.bindType ) || type; - - // Update special based on newly reset type - special = jQuery.event.special[ type ] || {}; - - // handleObj is passed to all event handlers - handleObj = jQuery.extend({ - type: type, - origType: origType, - data: data, - handler: handler, - guid: handler.guid, - selector: selector, - needsContext: selector && jQuery.expr.match.needsContext.test( selector ), - namespace: namespaces.join(".") - }, handleObjIn ); - - // Init the event handler queue if we're the first - if ( !(handlers = events[ type ]) ) { - handlers = events[ type ] = []; - handlers.delegateCount = 0; - - // Only use addEventListener if the special events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { - if ( elem.addEventListener ) { - elem.addEventListener( type, eventHandle, false ); - } - } - } - - if ( special.add ) { - special.add.call( elem, handleObj ); - - if ( !handleObj.handler.guid ) { - handleObj.handler.guid = handler.guid; - } - } - - // Add to the element's handler list, delegates in front - if ( selector ) { - handlers.splice( handlers.delegateCount++, 0, handleObj ); - } else { - handlers.push( handleObj ); - } - - // Keep track of which events have ever been used, for event optimization - jQuery.event.global[ type ] = true; - } - - }, - - // Detach an event or set of events from an element - remove: function( elem, types, handler, selector, mappedTypes ) { - - var j, origCount, tmp, - events, t, handleObj, - special, handlers, type, namespaces, origType, - elemData = data_priv.hasData( elem ) && data_priv.get( elem ); - - if ( !elemData || !(events = elemData.events) ) { - return; - } - - // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( rnotwhite ) || [ "" ]; - t = types.length; - while ( t-- ) { - tmp = rtypenamespace.exec( types[t] ) || []; - type = origType = tmp[1]; - namespaces = ( tmp[2] || "" ).split( "." ).sort(); - - // Unbind all events (on this namespace, if provided) for the element - if ( !type ) { - for ( type in events ) { - jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); - } - continue; - } - - special = jQuery.event.special[ type ] || {}; - type = ( selector ? special.delegateType : special.bindType ) || type; - handlers = events[ type ] || []; - tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); - - // Remove matching events - origCount = j = handlers.length; - while ( j-- ) { - handleObj = handlers[ j ]; - - if ( ( mappedTypes || origType === handleObj.origType ) && - ( !handler || handler.guid === handleObj.guid ) && - ( !tmp || tmp.test( handleObj.namespace ) ) && - ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { - handlers.splice( j, 1 ); - - if ( handleObj.selector ) { - handlers.delegateCount--; - } - if ( special.remove ) { - special.remove.call( elem, handleObj ); - } - } - } - - // Remove generic event handler if we removed something and no more handlers exist - // (avoids potential for endless recursion during removal of special event handlers) - if ( origCount && !handlers.length ) { - if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { - jQuery.removeEvent( elem, type, elemData.handle ); - } - - delete events[ type ]; - } - } - - // Remove the expando if it's no longer used - if ( jQuery.isEmptyObject( events ) ) { - delete elemData.handle; - data_priv.remove( elem, "events" ); - } - }, - - trigger: function( event, data, elem, onlyHandlers ) { - - var i, cur, tmp, bubbleType, ontype, handle, special, - eventPath = [ elem || document ], - type = hasOwn.call( event, "type" ) ? event.type : event, - namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; - - cur = tmp = elem = elem || document; - - // Don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // focus/blur morphs to focusin/out; ensure we're not firing them right now - if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { - return; - } - - if ( type.indexOf(".") >= 0 ) { - // Namespaced trigger; create a regexp to match event type in handle() - namespaces = type.split("."); - type = namespaces.shift(); - namespaces.sort(); - } - ontype = type.indexOf(":") < 0 && "on" + type; - - // Caller can pass in a jQuery.Event object, Object, or just an event type string - event = event[ jQuery.expando ] ? - event : - new jQuery.Event( type, typeof event === "object" && event ); - - // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) - event.isTrigger = onlyHandlers ? 2 : 3; - event.namespace = namespaces.join("."); - event.namespace_re = event.namespace ? - new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : - null; - - // Clean up the event in case it is being reused - event.result = undefined; - if ( !event.target ) { - event.target = elem; - } - - // Clone any incoming data and prepend the event, creating the handler arg list - data = data == null ? - [ event ] : - jQuery.makeArray( data, [ event ] ); - - // Allow special events to draw outside the lines - special = jQuery.event.special[ type ] || {}; - if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { - return; - } - - // Determine event propagation path in advance, per W3C events spec (#9951) - // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) - if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { - - bubbleType = special.delegateType || type; - if ( !rfocusMorph.test( bubbleType + type ) ) { - cur = cur.parentNode; - } - for ( ; cur; cur = cur.parentNode ) { - eventPath.push( cur ); - tmp = cur; - } - - // Only add window if we got to document (e.g., not plain obj or detached DOM) - if ( tmp === (elem.ownerDocument || document) ) { - eventPath.push( tmp.defaultView || tmp.parentWindow || window ); - } - } - - // Fire handlers on the event path - i = 0; - while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { - - event.type = i > 1 ? - bubbleType : - special.bindType || type; - - // jQuery handler - handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); - if ( handle ) { - handle.apply( cur, data ); - } - - // Native handler - handle = ontype && cur[ ontype ]; - if ( handle && handle.apply && jQuery.acceptData( cur ) ) { - event.result = handle.apply( cur, data ); - if ( event.result === false ) { - event.preventDefault(); - } - } - } - event.type = type; - - // If nobody prevented the default action, do it now - if ( !onlyHandlers && !event.isDefaultPrevented() ) { - - if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && - jQuery.acceptData( elem ) ) { - - // Call a native DOM method on the target with the same name name as the event. - // Don't do default actions on window, that's where global variables be (#6170) - if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { - - // Don't re-trigger an onFOO event when we call its FOO() method - tmp = elem[ ontype ]; - - if ( tmp ) { - elem[ ontype ] = null; - } - - // Prevent re-triggering of the same event, since we already bubbled it above - jQuery.event.triggered = type; - elem[ type ](); - jQuery.event.triggered = undefined; - - if ( tmp ) { - elem[ ontype ] = tmp; - } - } - } - } - - return event.result; - }, - - dispatch: function( event ) { - - // Make a writable jQuery.Event from the native event object - event = jQuery.event.fix( event ); - - var i, j, ret, matched, handleObj, - handlerQueue = [], - args = slice.call( arguments ), - handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], - special = jQuery.event.special[ event.type ] || {}; - - // Use the fix-ed jQuery.Event rather than the (read-only) native event - args[0] = event; - event.delegateTarget = this; - - // Call the preDispatch hook for the mapped type, and let it bail if desired - if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { - return; - } - - // Determine handlers - handlerQueue = jQuery.event.handlers.call( this, event, handlers ); - - // Run delegates first; they may want to stop propagation beneath us - i = 0; - while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { - event.currentTarget = matched.elem; - - j = 0; - while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { - - // Triggered event must either 1) have no namespace, or - // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). - if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { - - event.handleObj = handleObj; - event.data = handleObj.data; - - ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) - .apply( matched.elem, args ); - - if ( ret !== undefined ) { - if ( (event.result = ret) === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - } - } - } - - // Call the postDispatch hook for the mapped type - if ( special.postDispatch ) { - special.postDispatch.call( this, event ); - } - - return event.result; - }, - - handlers: function( event, handlers ) { - var i, matches, sel, handleObj, - handlerQueue = [], - delegateCount = handlers.delegateCount, - cur = event.target; - - // Find delegate handlers - // Black-hole SVG instance trees (#13180) - // Avoid non-left-click bubbling in Firefox (#3861) - if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { - - for ( ; cur !== this; cur = cur.parentNode || this ) { - - // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) - if ( cur.disabled !== true || event.type !== "click" ) { - matches = []; - for ( i = 0; i < delegateCount; i++ ) { - handleObj = handlers[ i ]; - - // Don't conflict with Object.prototype properties (#13203) - sel = handleObj.selector + " "; - - if ( matches[ sel ] === undefined ) { - matches[ sel ] = handleObj.needsContext ? - jQuery( sel, this ).index( cur ) >= 0 : - jQuery.find( sel, this, null, [ cur ] ).length; - } - if ( matches[ sel ] ) { - matches.push( handleObj ); - } - } - if ( matches.length ) { - handlerQueue.push({ elem: cur, handlers: matches }); - } - } - } - } - - // Add the remaining (directly-bound) handlers - if ( delegateCount < handlers.length ) { - handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); - } - - return handlerQueue; - }, - - // Includes some event props shared by KeyEvent and MouseEvent - props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), - - fixHooks: {}, - - keyHooks: { - props: "char charCode key keyCode".split(" "), - filter: function( event, original ) { - - // Add which for key events - if ( event.which == null ) { - event.which = original.charCode != null ? original.charCode : original.keyCode; - } - - return event; - } - }, - - mouseHooks: { - props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), - filter: function( event, original ) { - var eventDoc, doc, body, - button = original.button; - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && original.clientX != null ) { - eventDoc = event.target.ownerDocument || document; - doc = eventDoc.documentElement; - body = eventDoc.body; - - event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); - event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && button !== undefined ) { - event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); - } - - return event; - } - }, - - fix: function( event ) { - if ( event[ jQuery.expando ] ) { - return event; - } - - // Create a writable copy of the event object and normalize some properties - var i, prop, copy, - type = event.type, - originalEvent = event, - fixHook = this.fixHooks[ type ]; - - if ( !fixHook ) { - this.fixHooks[ type ] = fixHook = - rmouseEvent.test( type ) ? this.mouseHooks : - rkeyEvent.test( type ) ? this.keyHooks : - {}; - } - copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; - - event = new jQuery.Event( originalEvent ); - - i = copy.length; - while ( i-- ) { - prop = copy[ i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Support: Cordova 2.5 (WebKit) (#13255) - // All events should have a target; Cordova deviceready doesn't - if ( !event.target ) { - event.target = document; - } - - // Support: Safari 6.0+, Chrome < 28 - // Target should not be a text node (#504, #13143) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; - }, - - special: { - load: { - // Prevent triggered image.load events from bubbling to window.load - noBubble: true - }, - focus: { - // Fire native event if possible so blur/focus sequence is correct - trigger: function() { - if ( this !== safeActiveElement() && this.focus ) { - this.focus(); - return false; - } - }, - delegateType: "focusin" - }, - blur: { - trigger: function() { - if ( this === safeActiveElement() && this.blur ) { - this.blur(); - return false; - } - }, - delegateType: "focusout" - }, - click: { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { - this.click(); - return false; - } - }, - - // For cross-browser consistency, don't fire native .click() on links - _default: function( event ) { - return jQuery.nodeName( event.target, "a" ); - } - }, - - beforeunload: { - postDispatch: function( event ) { - - // Support: Firefox 20+ - // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined && event.originalEvent ) { - event.originalEvent.returnValue = event.result; - } - } - } - }, - - simulate: function( type, elem, event, bubble ) { - // Piggyback on a donor event to simulate a different one. - // Fake originalEvent to avoid donor's stopPropagation, but if the - // simulated event prevents default then we do the same on the donor. - var e = jQuery.extend( - new jQuery.Event(), - event, - { - type: type, - isSimulated: true, - originalEvent: {} - } - ); - if ( bubble ) { - jQuery.event.trigger( e, null, elem ); - } else { - jQuery.event.dispatch.call( elem, e ); - } - if ( e.isDefaultPrevented() ) { - event.preventDefault(); - } - } -}; - -jQuery.removeEvent = function( elem, type, handle ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, handle, false ); - } -}; - -jQuery.Event = function( src, props ) { - // Allow instantiation without the 'new' keyword - if ( !(this instanceof jQuery.Event) ) { - return new jQuery.Event( src, props ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - - // Events bubbling up the document may have been marked as prevented - // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = src.defaultPrevented || - src.defaultPrevented === undefined && - // Support: Android < 4.0 - src.returnValue === false ? - returnTrue : - returnFalse; - - // Event type - } else { - this.type = src; - } - - // Put explicitly provided properties onto the event object - if ( props ) { - jQuery.extend( this, props ); - } - - // Create a timestamp if incoming event doesn't have one - this.timeStamp = src && src.timeStamp || jQuery.now(); - - // Mark it as fixed - this[ jQuery.expando ] = true; -}; - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse, - - preventDefault: function() { - var e = this.originalEvent; - - this.isDefaultPrevented = returnTrue; - - if ( e && e.preventDefault ) { - e.preventDefault(); - } - }, - stopPropagation: function() { - var e = this.originalEvent; - - this.isPropagationStopped = returnTrue; - - if ( e && e.stopPropagation ) { - e.stopPropagation(); - } - }, - stopImmediatePropagation: function() { - var e = this.originalEvent; - - this.isImmediatePropagationStopped = returnTrue; - - if ( e && e.stopImmediatePropagation ) { - e.stopImmediatePropagation(); - } - - this.stopPropagation(); - } -}; - -// Create mouseenter/leave events using mouseover/out and event-time checks -// Support: Chrome 15+ -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout", - pointerenter: "pointerover", - pointerleave: "pointerout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - delegateType: fix, - bindType: fix, - - handle: function( event ) { - var ret, - target = this, - related = event.relatedTarget, - handleObj = event.handleObj; - - // For mousenter/leave call the handler if related is outside the target. - // NB: No relatedTarget if the mouse left/entered the browser window - if ( !related || (related !== target && !jQuery.contains( target, related )) ) { - event.type = handleObj.origType; - ret = handleObj.handler.apply( this, arguments ); - event.type = fix; - } - return ret; - } - }; -}); - -// Create "bubbling" focus and blur events -// Support: Firefox, Chrome, Safari -if ( !support.focusinBubbles ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this, - attaches = data_priv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - data_priv.remove( doc, fix ); - - } else { - data_priv.access( doc, fix, attaches ); - } - } - }; - }); -} - -jQuery.fn.extend({ - - on: function( types, selector, data, fn, /*INTERNAL*/ one ) { - var origFn, type; - - // Types can be a map of types/handlers - if ( typeof types === "object" ) { - // ( types-Object, selector, data ) - if ( typeof selector !== "string" ) { - // ( types-Object, data ) - data = data || selector; - selector = undefined; - } - for ( type in types ) { - this.on( type, selector, data, types[ type ], one ); - } - return this; - } - - if ( data == null && fn == null ) { - // ( types, fn ) - fn = selector; - data = selector = undefined; - } else if ( fn == null ) { - if ( typeof selector === "string" ) { - // ( types, selector, fn ) - fn = data; - data = undefined; - } else { - // ( types, data, fn ) - fn = data; - data = selector; - selector = undefined; - } - } - if ( fn === false ) { - fn = returnFalse; - } else if ( !fn ) { - return this; - } - - if ( one === 1 ) { - origFn = fn; - fn = function( event ) { - // Can use an empty set, since event contains the info - jQuery().off( event ); - return origFn.apply( this, arguments ); - }; - // Use same guid so caller can remove using origFn - fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); - } - return this.each( function() { - jQuery.event.add( this, types, fn, data, selector ); - }); - }, - one: function( types, selector, data, fn ) { - return this.on( types, selector, data, fn, 1 ); - }, - off: function( types, selector, fn ) { - var handleObj, type; - if ( types && types.preventDefault && types.handleObj ) { - // ( event ) dispatched jQuery.Event - handleObj = types.handleObj; - jQuery( types.delegateTarget ).off( - handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, - handleObj.selector, - handleObj.handler - ); - return this; - } - if ( typeof types === "object" ) { - // ( types-object [, selector] ) - for ( type in types ) { - this.off( type, selector, types[ type ] ); - } - return this; - } - if ( selector === false || typeof selector === "function" ) { - // ( types [, fn] ) - fn = selector; - selector = undefined; - } - if ( fn === false ) { - fn = returnFalse; - } - return this.each(function() { - jQuery.event.remove( this, types, fn, selector ); - }); - }, - - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - triggerHandler: function( type, data ) { - var elem = this[0]; - if ( elem ) { - return jQuery.event.trigger( type, data, elem, true ); - } - } -}); - - -var - rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, - rtagName = /<([\w:]+)/, - rhtml = /<|&#?\w+;/, - rnoInnerhtml = /<(?:script|style|link)/i, - // checked="checked" or checked - rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, - rscriptType = /^$|\/(?:java|ecma)script/i, - rscriptTypeMasked = /^true\/(.*)/, - rcleanScript = /^\s*\s*$/g, - - // We have to close these tags to support XHTML (#13200) - wrapMap = { - - // Support: IE 9 - option: [ 1, "" ], - - thead: [ 1, "", "
    " ], - col: [ 2, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - - _default: [ 0, "", "" ] - }; - -// Support: IE 9 -wrapMap.optgroup = wrapMap.option; - -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// Support: 1.x compatibility -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = data_priv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Support: IE >= 9 -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} - -jQuery.extend({ - clone: function( elem, dataAndEvents, deepDataAndEvents ) { - var i, l, srcElements, destElements, - clone = elem.cloneNode( true ), - inPage = jQuery.contains( elem.ownerDocument, elem ); - - // Support: IE >= 9 - // Fix Cloning issues - if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && - !jQuery.isXMLDoc( elem ) ) { - - // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 - destElements = getAll( clone ); - srcElements = getAll( elem ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - fixInput( srcElements[ i ], destElements[ i ] ); - } - } - - // Copy the events from the original to the clone - if ( dataAndEvents ) { - if ( deepDataAndEvents ) { - srcElements = srcElements || getAll( elem ); - destElements = destElements || getAll( clone ); - - for ( i = 0, l = srcElements.length; i < l; i++ ) { - cloneCopyEvent( srcElements[ i ], destElements[ i ] ); - } - } else { - cloneCopyEvent( elem, clone ); - } - } - - // Preserve script evaluation history - destElements = getAll( clone, "script" ); - if ( destElements.length > 0 ) { - setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); - } - - // Return the cloned set - return clone; - }, - - buildFragment: function( elems, context, scripts, selection ) { - var elem, tmp, tag, wrap, contains, j, - fragment = context.createDocumentFragment(), - nodes = [], - i = 0, - l = elems.length; - - for ( ; i < l; i++ ) { - elem = elems[ i ]; - - if ( elem || elem === 0 ) { - - // Add nodes directly - if ( jQuery.type( elem ) === "object" ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); - - // Convert non-html into a text node - } else if ( !rhtml.test( elem ) ) { - nodes.push( context.createTextNode( elem ) ); - - // Convert html into DOM nodes - } else { - tmp = tmp || fragment.appendChild( context.createElement("div") ); - - // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); - wrap = wrapMap[ tag ] || wrapMap._default; - tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; - - // Descend through wrappers to the right content - j = wrap[ 0 ]; - while ( j-- ) { - tmp = tmp.lastChild; - } - - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( nodes, tmp.childNodes ); - - // Remember the top-level container - tmp = fragment.firstChild; - - // Fixes #12346 - // Support: Webkit, IE - tmp.textContent = ""; - } - } - } - - // Remove wrapper from fragment - fragment.textContent = ""; - - i = 0; - while ( (elem = nodes[ i++ ]) ) { - - // #4087 - If origin and destination elements are the same, and this is - // that element, do not do anything - if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { - continue; - } - - contains = jQuery.contains( elem.ownerDocument, elem ); - - // Append to fragment - tmp = getAll( fragment.appendChild( elem ), "script" ); - - // Preserve script evaluation history - if ( contains ) { - setGlobalEval( tmp ); - } - - // Capture executables - if ( scripts ) { - j = 0; - while ( (elem = tmp[ j++ ]) ) { - if ( rscriptType.test( elem.type || "" ) ) { - scripts.push( elem ); - } - } - } - } - - return fragment; - }, - - cleanData: function( elems ) { - var data, elem, type, key, - special = jQuery.event.special, - i = 0; - - for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( jQuery.acceptData( elem ) ) { - key = elem[ data_priv.expando ]; - - if ( key && (data = data_priv.cache[ key ]) ) { - if ( data.events ) { - for ( type in data.events ) { - if ( special[ type ] ) { - jQuery.event.remove( elem, type ); - - // This is a shortcut to avoid jQuery.event.remove's overhead - } else { - jQuery.removeEvent( elem, type, data.handle ); - } - } - } - if ( data_priv.cache[ key ] ) { - // Discard any remaining `private` data - delete data_priv.cache[ key ]; - } - } - } - // Discard any remaining `user` data - delete data_user.cache[ elem[ data_user.expando ] ]; - } - } -}); - -jQuery.fn.extend({ - text: function( value ) { - return access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().each(function() { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - this.textContent = value; - } - }); - }, null, value, arguments.length ); - }, - - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, - - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, - - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - remove: function( selector, keepData /* Internal Use Only */ ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map(function() { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var arg = arguments[ 0 ]; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - arg = this.parentNode; - - jQuery.cleanData( getAll( this ) ); - - if ( arg ) { - arg.replaceChild( elem, this ); - } - }); - - // Force removal if there was no new content (e.g., from empty arguments) - return arg && (arg.length || arg.nodeType) ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback ) { - - // Flatten any nested arrays - args = concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || - ( l > 1 && typeof value === "string" && - !support.checkClone && rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - self.domManip( args, callback ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Optional AJAX dependency, but won't run scripts if not present - if ( jQuery._evalUrl ) { - jQuery._evalUrl( node.src ); - } - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; - } -}); - -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; - - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); - - // Support: QtWebKit - // .get() because push.apply(_, arraylike) throws - push.apply( ret, elems.get() ); - } - - return this.pushStack( ret ); - }; -}); - - -var iframe, - elemdisplay = {}; - -/** - * Retrieve the actual display of a element - * @param {String} name nodeName of the element - * @param {Object} doc Document object - */ -// Called only from within defaultDisplay -function actualDisplay( name, doc ) { - var style, - elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), - - // getDefaultComputedStyle might be reliably used only on attached element - display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? - - // Use of this method is a temporary fix (more like optmization) until something better comes along, - // since it was removed from specification and supported only in FF - style.display : jQuery.css( elem[ 0 ], "display" ); - - // We don't have any data stored on the element, - // so use "detach" method as fast way to get rid of the element - elem.detach(); - - return display; -} - -/** - * Try to determine the default display value of an element - * @param {String} nodeName - */ -function defaultDisplay( nodeName ) { - var doc = document, - display = elemdisplay[ nodeName ]; - - if ( !display ) { - display = actualDisplay( nodeName, doc ); - - // If the simple way fails, read from inside an iframe - if ( display === "none" || !display ) { - - // Use the already-created iframe if possible - iframe = (iframe || jQuery( "