From 1fe2877a1472bfbfb81f720896991c13c4faafb8 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 28 Dec 2012 10:49:32 -0600 Subject: [PATCH 01/75] removed commit error From 60093e81b07ffb57548df3c0eac26557e10b6e7a Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 28 Dec 2012 15:45:14 -0600 Subject: [PATCH 02/75] debugger patch, thanks Mariano --- VERSION | 2 +- applications/admin/controllers/debug.py | 23 +++++++++++ applications/admin/static/js/ajax_editor.js | 45 ++++++++++++++++++++- applications/admin/views/default/edit.html | 19 +++++++-- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/VERSION b/VERSION index c163047c..b3a2beef 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.28.01.21.20 +Version 2.4.1-alpha.2+timestamp.2012.12.28.15.44.29 diff --git a/applications/admin/controllers/debug.py b/applications/admin/controllers/debug.py index f02b2866..97ef49f8 100644 --- a/applications/admin/controllers/debug.py +++ b/applications/admin/controllers/debug.py @@ -212,3 +212,26 @@ def toggle_breakpoint(): except Exception, e: session.flash = str(e) return response.json({'ok': ok, 'lineno': lineno}) + +def list_breakpoints(): + "Return a list of linenumbers for current breakpoints" + + breakpoints = [] + ok = None + try: + filename = os.path.join(request.env['applications_parent'], + 'applications', request.vars.filename) + # normalize path name: replace slashes, references, etc... + filename = os.path.normpath(os.path.normcase(filename)) + for bp in qdb_debugger.do_list_breakpoint(): + no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp + # normalize path name: replace slashes, references, etc... + bp_filename = os.path.normpath(os.path.normcase(bp_filename)) + if filename == bp_filename: + breakpoints.append(bp_lineno) + ok = True + except Exception, e: + session.flash = str(e) + ok = False + return response.json({'ok': ok, 'breakpoints': breakpoints}) + diff --git a/applications/admin/static/js/ajax_editor.js b/applications/admin/static/js/ajax_editor.js index b24b2fd0..44d5de7d 100644 --- a/applications/admin/static/js/ajax_editor.js +++ b/applications/admin/static/js/ajax_editor.js @@ -148,8 +148,12 @@ function getSelectionRange() { return sel; } -function doToggleBreakpoint(filename, url) { - var sel = getSelectionRange(); +function doToggleBreakpoint(filename, url, sel) { + if (sel==null) { + // use cursor position to determine the breakpoint line + // (gutter already tell us the selected line) + sel = getSelectionRange(); + } var dataForPost = prepareMultiPartPOST(new Array( prepareDataForSave('filename', filename), prepareDataForSave('sel_start', sel["start"]), @@ -199,6 +203,43 @@ function doToggleBreakpoint(filename, url) { return false; } +// on load, update all breakpoints markers: +function doListBreakpoints(filename, url) { + var dataForPost = prepareMultiPartPOST(new Array( + prepareDataForSave('filename', filename) + )); + jQuery.ajax({ + type: "POST", + contentType: 'multipart/form-data;boundary="'+dataForPost[1]+'"', + url: url, + dataType: "json", + data: dataForPost[0], + timeout: 5000, + beforeSend: function(xhr) { + xhr.setRequestHeader('web2py-component-location', + document.location); + xhr.setRequestHeader('web2py-component-element', + 'doListBreakpoints');}, + success: function(json,text,xhr){ + try { + if (json.error) { + window.location.href=json.redirect; + } else { + if (window.mirror) { + for (i in json.breakpoints) { + lineno = json.breakpoints[i]; + // mark the breakpoint if ok=True + editor.setMarker(lineno-1, + " %N%"); + } + } + } + } catch(e) { on_error(); } + }, + error: function(json) { on_error(); } + }); + return false; +} function keepalive(url) { jQuery.ajax({ diff --git a/applications/admin/views/default/edit.html b/applications/admin/views/default/edit.html index 19c416e4..cb1b6035 100644 --- a/applications/admin/views/default/edit.html +++ b/applications/admin/views/default/edit.html @@ -25,6 +25,13 @@ + {{elif TEXT_EDITOR == 'ace':}} @@ -103,7 +110,7 @@ jQuery(document).ready(function(){ {{if filetype=='python':}} {{=A(SPAN(T('toggle breakpoint')), _value="breakpoint", _name="breakpoint", - _onclick="return doToggleBreakpoint('%s','%s://%s%s');" % (filename, + _onclick="return doToggleBreakpoint('%s','%s://%s%s',null);" % (filename, request.env['wsgi_url_scheme'], request.env['http_host'], URL(c='debug', f='toggle_breakpoint')), _class="button special btn btn-inverse")}} @@ -202,7 +209,13 @@ jQuery(document).ready(function(){ autofocus: true, onCursorActivity: function() { editor.setLineClass(hlLine, null, null); - hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline");} + hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline");}, + onGutterClick: function(cm, n) { + sel = {start: n, end: n, data: ''}; + doToggleBreakpoint({{=XML("'%s','%s://%s%s',sel" % (filename, + request.env['wsgi_url_scheme'], request.env['http_host'], + URL(c='debug', f='toggle_breakpoint')))}}); + } }; var editor = CodeMirror.fromTextArea( document.getElementById("body"),cm_opts); @@ -293,4 +306,4 @@ window.onload = function() { {{pass}} - \ No newline at end of file + From 9557c46dcf54383d1b54111cc22d11cb8b1819c4 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 28 Dec 2012 16:19:50 -0600 Subject: [PATCH 03/75] fixed redirection in login page, thanks Osman Masood --- VERSION | 2 +- applications/welcome/views/default/user.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index b3a2beef..25bf7f55 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.28.15.44.29 +Version 2.4.1-alpha.2+timestamp.2012.12.28.16.18.51 diff --git a/applications/welcome/views/default/user.html b/applications/welcome/views/default/user.html index 475be5b3..b4bd0a15 100644 --- a/applications/welcome/views/default/user.html +++ b/applications/welcome/views/default/user.html @@ -4,7 +4,7 @@ {{ if request.args(0)=='login': if not 'register' in auth.settings.actions_disabled: - form.add_button(T('Register'),URL(args='register'),_class='btn') + form.add_button(T('Register'),URL(args='register', vars={'_next': request.vars._next} if request.vars._next else None),_class='btn') pass if not 'request_reset_password' in auth.settings.actions_disabled: form.add_button(T('Lost Password'),URL(args='request_reset_password'),_class='btn') From 52c1ba67e33ebdef20268c2e31141cc70185ac3f Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 30 Dec 2012 15:16:39 -0600 Subject: [PATCH 04/75] added setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh, thanks Richard --- VERSION | 2 +- ...2-04-redmine-unicorn-web2py-uwsgi-nginx.sh | 464 ++++++++++++++++++ 2 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 scripts/setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh diff --git a/VERSION b/VERSION index 25bf7f55..c532e5e6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.28.16.18.51 +Version 2.4.1-alpha.2+timestamp.2012.12.30.15.15.53 diff --git a/scripts/setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh b/scripts/setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh new file mode 100644 index 00000000..84a0b0f2 --- /dev/null +++ b/scripts/setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh @@ -0,0 +1,464 @@ +#!/bin/bash + +# ------------------------------------------------------------------------------ +# Description : Installation and basic configuration of web2py, uWSGI, Redmine, +# Unicorn, Nginx and PostgreSQL. +# Usage : Copy the script in /home/username and run it as root, you may +# need to allow exectuion (chmod +x). Ex.: +# sudo ./setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh +# File : setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh +# Author : Richard V?zina +# Email : ml.richard.vezina@gmail.com +# Copyright : Richard V?zina +# Date : ven 28 d?c 2012 13:27:11 EST +# Disclaimers : This script is provided "as is", without warranty of any kind. +# Licence : CC BY-NC 2.5 CA +# ------------------------------------------------------------------------------ + +echo 'setup-ubuntu-12-04-redmine-unicorn-web2py-uwsgi-nginx.sh' +echo 'Requires Ubuntu = 12.04 (May works with 12.10 not tested) and installs Redmine + Unicorn + Web2py + uWSGI + Nginx + PostgreSQL' +# Check if user has root privileges +if [[ $EUID -ne 0 ]]; then + echo "You must run the script as root or using sudo" + exit 1 +fi + +# ------------------------------------------------------------------------------ +# We concentrate here user prompts!! +# Get Redmine Postgres Database Password +echo -e "Redmine Postgres Database Password: \c " +read REDMINEPASSWORD +# Get Web2py Admin Password +echo -e "Web2py Admin Password: \c " +read PW + +cd ~ +openssl genrsa 1024 > self_signed.key +chmod 400 self_signed.key +openssl req -new -x509 -nodes -sha1 -days 1780 -key self_signed.key > self_signed.cert +openssl x509 -noout -fingerprint -text < self_signed.cert > self_signed.info +# ------------------------------------------------------------------------------ + +apt-get update +apt-get -y upgrade +apt-get autoremove +apt-get autoclean +apt-get -y install postgresql +apt-get -y install nginx-full +apt-get -y install build-essential python-dev libxml2-dev python-pip unzip +apt-get -y install ruby1.9.3 # Ref.: http://askubuntu.com/questions/137485/rails-3-not-using-rvm +apt-get -y install libpq-dev # Required for gem1.9.3 install pg Ref.: http://stackoverflow.com/questions/6040583/unable-to-install-pg-gem-on-ubuntu-cant-find-the-libpq-fe-h-header + +gem1.9.3 install rails --no-rdoc --no-ri # For testing (faster) --no-rdoc --no-ri +gem1.9.3 install unicorn --no-rdoc --no-ri # For testing (faster) --no-rdoc --no-ri +gem1.9.3 install pg --no-rdoc --no-ri # For testing (faster) --no-rdoc --no-ri +cd /opt +wget http://rubyforge.org/frs/download.php/76627/redmine-2.2.0.tar.gz +wget http://rubyforge.org/frs/download.php/76628/redmine-2.2.0.tar.gz.md5 +md5sum --check redmine-2.2.0.tar.gz.md5 > redmine_md5_checked_successfully +if [ -f redmine_md5_checked_successfully ] +then + tar xvfz redmine-2.2.0.tar.gz + rm redmine_md5_checked_successfully +else + echo "Redmine md5 check sum failed..." + exit 1 +fi +cd redmine-2.2.0 +bundle install --without development test rmagick sqlite mysql +mkdir /var/www +ln -s /opt/redmine-2.2.0/public /var/www/redmine +chown -R www-data.www-data /var/www +chown -R www-data.www-data /opt/redmine-2.2.0/public +# To avoid prompt during execution of the script use psql instead of createuser +#echo "Enter a postgres redmine user password twice:" +#createuser -P -S -D -R -l -e redmine +# createuser switch: -P --pwprompt -S --no-superuser -D --no-createdb -R --no-createrole -l --login -e --echo +sudo -u postgres psql -c "CREATE ROLE redmine LOGIN; ALTER ROLE redmine WITH ENCRYPTED PASSWORD '$REDMINEPASSWORD';" +# createdb wouldn't work without having root password +#createdb -U postgres -w -E UTF8 -O redmine -e redmine +# createdb switch: -U username --username=username -w --no-password -E Encoding -O owner --owner=owner -e --echo +sudo -u postgres psql -c "CREATE DATABASE redmine WITH ENCODING='UTF8' OWNER=redmine;" +cd /opt/redmine-2.2.0/config +# Here we change related to an issue with new rails version as far as I understand +# Ref1.: http://www.redmine.org/projects/redmine/wiki/HowTo_Install_Redmine_in_a_sub-URI # Preferred solution used +# Ref2.: http://www.redmine.org/issues/12102 # JS and CSS was not working until I add this line 'RedmineApp::Application.routes.default_scope = { :path => "/redmine", :shallow_path => "/redmine" }' before 'RedmineApp::Application.initialize!' +cp environment.rb environment.rb_original # Backup default environment.rb +sed '/RedmineApp::Application.initialize!/c \RedmineApp::Application.routes.default_scope = { :path => "/redmine", :shallow_path => "/redmine" }\nRedmineApp::Application.initialize!\nRedmine::Utils::relative_url_root = "/redmine"' environment.rb_original > environment.rb +# Now we configure Redmine database access +#nano database.yml +# paste : +echo 'production: + adapter: postgresql + database: redmine + host: localhost + username: redmine + password: "'$REDMINEPASSWORD'" + encoding: utf8' > database.yml +rake generate_secret_token +RAILS_ENV=production rake db:migrate +RAILS_ENV=production rake redmine:load_default_data +mkdir /opt/redmine-2.2.0/tmp/pids +#mkdir /opt/redmine-2.2.0/log # if not there +cd /opt/redmine-2.2.0/config +# Create Unicorn specific Redmine config in /opt/redmine-2.2.0/config/unicorn.rb +echo '#unicorn.rb Starts here +worker_processes 1 +working_directory "/opt/redmine-2.2.0" # needs to be the correct directory for redmine + +# This loads the application in the master process before forking +# worker processes +# Read more about it here: +# http://unicorn.bogomips.org/Unicorn/Configurator.html +preload_app true +timeout 45 + +# This is where we specify the socket. +# We will point the upstream Nginx module to this socket later on +listen "/tmp/unicorn_rails.socket", :backlog => 64 #directory structure needs to be created. +pid "/opt/redmine-2.2.0/tmp/pids/unicorn_rails.pid" # make sure this points to a valid directory. Make sure it is named the same as the real process name in order to allow init.d script start-stop-daemon command to kill unicorn process properly + +# Set the path of the log files inside the log folder of the testapp +stderr_path "/opt/redmine-2.2.0/log/unicorn_rails.stderr.log" +stdout_path "/opt/redmine-2.2.0/log/unicorn_rails.stdout.log" + +before_fork do |server, worker| +# This option works in together with preload_app true setting +# What is does is prevent the master process from holding +# the database connection +defined?(ActiveRecord::Base) and +ActiveRecord::Base.connection.disconnect! +end + +after_fork do |server, worker| +# Here we are establishing the connection after forking worker +# processes +defined?(ActiveRecord::Base) and +ActiveRecord::Base.establish_connection +# change below if your redmine instance is running differently +worker.user('\''www-data'\'', '\''www-data'\'') if Process.euid == 0 +end +#unicorn.rb Ends here' > unicorn.rb +chown www-data:www-data unicorn.rb +chown -R www-data:www-data /opt/redmine-2.2.0/tmp +mkdir /etc/unicorn +# Set some config for Unicorn in /etc/unicorn/redmine +echo 'RAILS_ROOT=/opt/redmine-2 +RAILS_ENV=production' > /etc/unicorn/redmine +# Create a Unicorn Redmine start script in /etc/init.d/redmine +echo '#! /bin/sh +### BEGIN INIT INFO +# Provides: redmine +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: redmine initscript +# Description: This script startup unicorn server and redmine and should +# be placed in /etc/init.d. +### END INIT INFO + +# ------------------------------------------------------------------------------ +# Author: Richard V?zina +# Base on Ubuntu 12.04 : /etc/init.d/skeleton +# ven 21 d?c 2012 11:08:31 EST +# ------------------------------------------------------------------------------ + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +APP=/opt/redmine-2.2.0/ +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Unicorn and Redmine" +NAME=unicorn_rails +DAEMON=/usr/local/bin/$NAME +DAEMON_ARGS=" -E production -c $APP/config/unicorn.rb -D" +PIDFILE=/opt/redmine-2.2.0/tmp/pids/$NAME.pid +SCRIPTNAME=/etc/init.d/redmine + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don'\''t delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave '\''force-reload'\'' as an alias for '\''restart'\''. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # '\''force-reload'\'' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac + +:' > /etc/init.d/redmine +chmod +x /etc/init.d/redmine +# Backup default Nginx site and replace it +cp /etc/nginx/sites-available/default /etc/nginx/sites-available/default_original +rm /etc/nginx/sites-available/default +# Create configuration file /etc/nginx/sites-available/default +echo 'upstream unicorn_server { + # This is the socket we configured in unicorn.rb + server unix:/tmp/unicorn_rails.socket + fail_timeout=0; +} +server { + listen 80; + #return 301 https://192.168.1.126$request_uri; # http://$hostname$request_uri; # idem #http://wiki.nginx.org/Pitfalls#Taxing_Rewrites + charset utf-8; + server_name localhost; # $hostname; + root /var/www; + access_log /var/log/nginx/yoursite.access.log; + error_log /var/log/nginx/yoursite.error.log; + #to enable correct use of response.static_version + #location ~* /(\w+)/static(?:/_[\d]+\.[\d]+\.[\d]+)?/(.*)$ { + # alias /home/www-data/web2py/applications/$1/static/$2; + # expires max; + #} + location ~* /(\w+)/static/ { + root /home/www-data/web2py/applications/; + #remove next comment on production + #expires max; + } + location ~^\/(?!redmine(.*)) { + #uwsgi_pass 127.0.0.1:9001; + uwsgi_pass unix:///tmp/web2py.socket; + include uwsgi_params; + uwsgi_param UWSGI_SCHEME $scheme; + uwsgi_param SERVER_SOFTWARE nginx/$nginx_version; + } + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + + if (!-f $request_filename) { + proxy_pass http://unicorn_server; + break; + } + } +} +server { + listen 443 default_server ssl; + charset utf-8; + server_name localhost; # $hostname; + root /var/www; + ssl_certificate /etc/nginx/ssl/self_signed.cert; + ssl_certificate_key /etc/nginx/ssl/self_signed.key; + ssl_prefer_server_ciphers on; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + ssl_ciphers ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA; + ssl_protocols SSLv3 TLSv1; + keepalive_timeout 70; + location ~^\/(?!redmine(.*)) { + #uwsgi_pass 127.0.0.1:9001; + uwsgi_pass unix:///tmp/web2py.socket; + include uwsgi_params; + uwsgi_param UWSGI_SCHEME $scheme; + uwsgi_param SERVER_SOFTWARE nginx/$nginx_version; + } + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + + if (!-f $request_filename) { + proxy_pass http://unicorn_server; + break; + } + } + +}' >/etc/nginx/sites-available/default + +#ln -s /etc/nginx/sites-available/web2py /etc/nginx/sites-enabled/web2py +#rm /etc/nginx/sites-enabled/default + +# We copy ssl files we previously created +if [ -f /etc/nginx/ssl ] +then + cp ~/self_signed.* /etc/nginx/ssl/ + rm ~/self_signed.* +else + mkdir /etc/nginx/ssl + cp ~/self_signed.* /etc/nginx/ssl/ + rm ~/self_signed.* +fi + +pip install --upgrade pip +PIPPATH=`which pip` +$PIPPATH install --upgrade uwsgi + +# Prepare folders for uwsgi +sudo mkdir /etc/uwsgi +sudo mkdir /var/log/uwsgi + +# Create configuration file /etc/uwsgi/web2py.xml +echo ' + /tmp/web2py.socket + /home/www-data/web2py/ + /=wsgihandler:application + + 4 + 60 + 8 + 1 + /tmp/stats.socket + 2000 + 512 + 256 + 192 + www-data + www-data + 0 0 -1 -1 -1 python /home/www-data/web2py/web2py.py -Q -S welcome -M -R scripts/sessions2trash.py -A -o + +' > /etc/uwsgi/web2py.xml + +#Create a configuration file for uwsgi in emperor-mode +#for Upstart in /etc/init/uwsgi-emperor.conf +echo '# Emperor uWSGI script + +description "uWSGI Emperor" +start on runlevel [2345] +stop on runlevel [06] +## +#remove the comments in the next section to enable static file compression for the welcome app +#in that case, turn on gzip_static on; on /etc/nginx/nginx.conf +## +#pre-start script +# python /home/www-data/web2py/web2py.py -S welcome -R scripts/zip_static_files.py +# chown -R www-data:www-data /home/www-data/web2py/* +#end script +respawn +exec uwsgi --master --die-on-term --emperor /etc/uwsgi --logto /var/log/uwsgi/uwsgi.log +' > /etc/init/uwsgi-emperor.conf +# Install Web2py +mkdir /home/www-data +cd /home/www-data +wget http://web2py.com/examples/static/web2py_src.zip +unzip web2py_src.zip +rm web2py_src.zip +# Download latest version of sessions2trash.py +wget http://web2py.googlecode.com/hg/scripts/sessions2trash.py -O /home/www-data/web2py/scripts/sessions2trash.py +chown -R www-data:www-data web2py +cd /home/www-data/web2py +sudo -u www-data python -c "from gluon.main import save_password; save_password('$PW',443)" +/etc/init.d/redmine start +start uwsgi-emperor +/etc/init.d/nginx restart +ufw allow 80 # Or check your firewall configuration From 117a0b9849f1bb47efb265623e7d56a594c7f6fd Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 31 Dec 2012 14:38:43 -0600 Subject: [PATCH 05/75] fixed issue 1247 Missing URL for Delete button, thanks Boris and Niphlod --- VERSION | 2 +- gluon/html.py | 4 ++-- gluon/sqlhtml.py | 40 +++++++++++++++++----------------------- 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/VERSION b/VERSION index c532e5e6..4ffd1152 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.30.15.15.53 +Version 2.4.1-alpha.2+timestamp.2012.12.31.14.38.01 diff --git a/gluon/html.py b/gluon/html.py index 09783e78..e38c4d82 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -1478,9 +1478,9 @@ class A(DIV): (self['component'], self['target'] or '', d) self['_href'] = self['_href'] or '#null' elif self['callback']: - returnfalse = "var e = arguments[0] || window.event; e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation();" + returnfalse = "var e = arguments[0] || window.event; e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); e.preventDefault();" if d: - self['_onclick'] = "if(confirm(w2p_ajax_confirm_message||'Are you sure you want o delete this object?')){ajax('%s',[],'%s');%s};%s" % \ + self['_onclick'] = "if(confirm(w2p_ajax_confirm_message||'Are you sure you want to delete this object?')){ajax('%s',[],'%s');%s};%s" % \ (self['callback'], self['target'] or '', d, returnfalse) else: self['_onclick'] = "ajax('%s',[],'%s');%sreturn false" % \ diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index a5827e96..f8228046 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1807,28 +1807,21 @@ class SQLFORM(FORM): buttonurl=url(args=[]), callback=None, delete=None, trap=True): if showbuttontext: - if callback: - return A(SPAN(_class=ui.get(buttonclass)), - SPAN(T(buttontext), _title=buttontext, - _class=ui.get('buttontext')), - callback=callback, delete=delete, - _class=trap_class(ui.get('button'), trap)) - else: - return A(SPAN(_class=ui.get(buttonclass)), - SPAN(T(buttontext), _title=buttontext, - _class=ui.get('buttontext')), - _href=buttonurl, - _class=trap_class(ui.get('button'), trap)) + return A(SPAN(_class=ui.get(buttonclass)), + SPAN(T(buttontext), _title=buttontext, + _class=ui.get('buttontext')), + _href=buttonurl, + callback=callback, + delete=delete, + _class=trap_class(ui.get('button'), trap)) else: - if callback: - return A(SPAN(_class=ui.get(buttonclass)), - callback=callback, delete=delete, - _title=buttontext, - _class=trap_class(ui.get('buttontext'), trap)) - else: - return A(SPAN(_class=ui.get(buttonclass)), - _href=buttonurl, _title=buttontext, - _class=trap_class(ui.get('buttontext'), trap)) + return A(SPAN(_class=ui.get(buttonclass)), + _href=buttonurl, + callback=callback, + delete=delete, + _title=buttontext, + _class=trap_class(ui.get('buttontext'), trap)) + dbset = db(query) tablenames = db._adapter.tables(dbset.query) if left is not None: @@ -1948,8 +1941,8 @@ class SQLFORM(FORM): table = db[request.args[-2]] if ondelete: ondelete(table, request.args[-1]) - ret = db(table[table._id.name] == request.args[-1]).delete() - return ret + db(table[table._id.name] == request.args[-1]).delete() + redirect(referrer) exportManager = dict( csv_with_hidden_cols=(ExporterCSV, 'CSV (hidden cols)'), @@ -2293,6 +2286,7 @@ class SQLFORM(FORM): if deletable and (not callable(deletable) or deletable(row)): row_buttons.append(gridbutton( 'buttondelete', 'Delete', + url(args=['delete', tablename, id]), callback=url(args=['delete', tablename, id]), delete='tr')) if buttons_placement in ['right', 'both']: From ed42c3e489917c9b78ca9f3ba2032ab38a95b52f Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 31 Dec 2012 15:17:55 -0600 Subject: [PATCH 06/75] fixed issue 1246, is_active and left joins --- VERSION | 2 +- gluon/dal.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/VERSION b/VERSION index 4ffd1152..19eb19b0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.31.14.38.01 +Version 2.4.1-alpha.2+timestamp.2012.12.31.15.17.11 diff --git a/gluon/dal.py b/gluon/dal.py index b4df67e9..4dff5484 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -1291,6 +1291,8 @@ class BaseAdapter(ConnectionPool): return '%s AS %s' % (self.expand(first), second) def ON(self, first, second): + if use_common_filters(second): + second = self.common_filter(second,[first._tablename]) return '%s ON %s' % (self.expand(first), self.expand(second)) def INVERT(self, first): @@ -1466,9 +1468,6 @@ class BaseAdapter(ConnectionPool): if not tablename in tablenames: tablenames.append(tablename) - if use_common_filters(query): - query = self.common_filter(query,tablenames) - if len(tablenames) < 1: raise SyntaxError('Set: no tables selected') self._colnames = map(self.expand, fields) @@ -1477,10 +1476,6 @@ class BaseAdapter(ConnectionPool): field = field.st_astext() return self.expand(field) sql_f = ', '.join(map(geoexpand, fields)) - if query: - sql_w = ' WHERE ' + self.expand(query) - else: - sql_w = '' sql_o = '' sql_s = '' left = args_get('left', False) @@ -1530,6 +1525,13 @@ class BaseAdapter(ConnectionPool): important_tablenames = joint + joinont + tables_to_merge.keys() excluded = [t for t in tablenames if not t in important_tablenames ] + else: + excluded = tablenames + + if use_common_filters(query): + query = self.common_filter(query,excluded) + sql_w = ' WHERE ' + self.expand(query) if query else '' + def alias(t): return str(self.db[t]) if inner_join and not left: From dd3055836a3f8fbc5b06763710c369cab3f3fc40 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 1 Jan 2013 19:03:44 -0600 Subject: [PATCH 07/75] fixed issue 1251, imporved mongodb support, thanks Alan --- VERSION | 2 +- gluon/dal.py | 197 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 129 insertions(+), 70 deletions(-) diff --git a/VERSION b/VERSION index 19eb19b0..6cb2c025 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2012.12.31.15.17.11 +Version 2.4.1-alpha.2+timestamp.2013.01.01.19.02.47 diff --git a/gluon/dal.py b/gluon/dal.py index 4dff5484..e5085787 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -4913,6 +4913,7 @@ class MongoDBAdapter(NoSQLAdapter): 'time': datetime.time, 'datetime': datetime.datetime, 'id': long, + 'mongo': unicode, # any Mongodb document (not implemented) 'reference': long, 'list:string': list, 'list:integer': list, @@ -4928,6 +4929,21 @@ class MongoDBAdapter(NoSQLAdapter): if do_connect: self.find_driver(adapter_args) m=None + + import random + try: + from pymongo.objectid import ObjectId + except ImportError: + from bson.objectid import ObjectId + try: + from bson.son import SON + except ImportError: + from pymongo.son import SON + + self.SON = SON + self.ObjectId = ObjectId + self.random = random + try: #Since version 2 import pymongo.uri_parser @@ -4971,12 +4987,46 @@ class MongoDBAdapter(NoSQLAdapter): raise SyntaxError("This is not an official Mongodb uri (http://www.mongodb.org/display/DOCS/Connections) Error : %s" % inst) self.reconnect(connector,cursor=False) + def object_id(self, arg=None): + """ Convert input to a valid Mongodb ObjectId instance + + self.object_id("") -> ObjectId (not unique) instance """ + if not arg: + arg = 0 + if isinstance(arg, basestring): + # we assume an integer as default input + rawhex = len(arg.replace("0x", "").replace("L", "")) == 24 + if arg.isdigit() and (not rawhex): + arg = int(arg) + elif arg == "": + arg = int("0x%sL" % \ + str("".join([self.random.choice("0123456789abcdef") for x in \ + range(24)])), 0) + elif arg.isalnum(): + if not arg.startswith("0x"): + arg = "0x%s" % arg + try: + arg = int(arg, 0) + except ValueError, e: + raise ValueError("invalid objectid argument string: %s" % e) + else: + raise ValueError("invalid objectid argument string. requires an integer or base 16 value") + elif isinstance(arg, self.ObjectId): + return arg + if not isinstance(arg, (int, long)): + raise TypeError("object_id argument must be of type ObjectId or an objectid representable integer") + if arg == 0: + hexvalue = "".zfill(24) + else: + hexvalue = hex(arg)[2:].replace("L", "") + return self.ObjectId(hexvalue) + def represent(self, obj, fieldtype): value = NoSQLAdapter.represent(self, obj, fieldtype) if fieldtype =='date': if value == None: return value - t = datetime.time(0, 0, 0)#this piece of data can be stripped of based on the fieldtype + t = datetime.time(0, 0, 0)#this piece of data can be stripped off based on the fieldtype return datetime.datetime.combine(value, t) #mongodb doesn't has a date object and so it must datetime, string or integer elif fieldtype == 'time': if value == None: @@ -4993,7 +5043,16 @@ class MongoDBAdapter(NoSQLAdapter): if safe==None: safe=self.safe ctable = self.connection[table._tablename] - values = dict((k.name,self.represent(v,table[k.name].type)) for k,v in fields) + values = dict() + for k, v in fields: + # avoid writing "id" name reserved form Mongodb + if not k.name == "id": + fieldname = k.name + fieldtype = table[k.name].type + if ("reference" in fieldtype) or (fieldtype=="id"): + values[fieldname] = self.object_id(v) + else: + values[fieldname] = self.represent(v, fieldtype) ctable.insert(values,safe=safe) return int(str(values['_id']), 16) @@ -5014,13 +5073,10 @@ class MongoDBAdapter(NoSQLAdapter): # therefor call __select() connection[table].find(query).count() Since this will probably reduce the return set? def expand(self, expression, field_type=None): - try: - from pymongo.objectid import ObjectId - except ImportError: - from bson.objectid import ObjectId #if isinstance(expression,Field): # if expression.type=='id': # return {_id}" + result = None if isinstance(expression, Query): # any query using 'id':= # set name as _id (as per pymongo/mongodb primary key) @@ -5028,61 +5084,42 @@ class MongoDBAdapter(NoSQLAdapter): # (if its not already) # if second arg is 0 convert to objectid if isinstance(expression.first,Field) and \ - expression.first.type == 'id': - expression.first.name = '_id' - if expression.second != 0 and \ - not isinstance(expression.second,ObjectId): - if isinstance(expression.second,int): - try: - # Because the reference field is by default - # an integer and therefore this must be an - # integer to be able to work with other - # databases - expression.second = ObjectId(("%X" % expression.second)) - except: - raise SyntaxError('The second argument must by an integer that can represent an objectid.') - else: - try: - #But a direct id is also possible - expression.second = ObjectId(expression.second) - except: - raise SyntaxError('second argument must be of type ObjectId or an objectid representable integer') - elif expression.second == 0: - expression.second = ObjectId('000000000000000000000000') - return expression.op(expression.first, expression.second) + ((expression.first.type == 'id') or \ + ("reference" in expression.first.type)): + if expression.first.type == 'id': + expression.first.name = '_id' + # cast to Mongo ObjectId + expression.second = self.object_id(expression.second) + result = expression.op(expression.first, expression.second) if isinstance(expression, Field): if expression.type=='id': - return "_id" + result = "_id" else: - return expression.name + result = expression.name #return expression elif isinstance(expression, (Expression, Query)): if not expression.second is None: - return expression.op(expression.first, expression.second) + result = expression.op(expression.first, expression.second) elif not expression.first is None: - return expression.op(expression.first) + result = expression.op(expression.first) elif not isinstance(expression.op, str): - return expression.op() + result = expression.op() else: - return expression.op + result = expression.op elif field_type: - return str(self.represent(expression,field_type)) + result = str(self.represent(expression,field_type)) elif isinstance(expression,(list,tuple)): - return ','.join(self.represent(item,field_type) for item in expression) + result = ','.join(self.represent(item,field_type) for item in expression) else: - return expression + result = expression + return result def _select(self,query,fields,attributes): - try: - from bson.son import SON - except ImportError: - from pymongo.son import SON - if 'for_update' in attributes: logging.warn('mongodb does not support for_update') for key in set(attributes.keys())-set(('limitby','orderby','for_update')): if attributes[key]!=None: - raise SyntaxError('invalid select attribute: %s' % key) + logging.warn('select attribute not implemented: %s' % key) new_fields=[] mongosort_list = [] @@ -5108,7 +5145,7 @@ class MongoDBAdapter(NoSQLAdapter): else: limitby_skip = limitby_limit = 0 - mongofields_dict = SON() + mongofields_dict = self.SON() mongoqry_dict = {} for item in fields: if isinstance(item,SQLALL): @@ -5132,13 +5169,10 @@ class MongoDBAdapter(NoSQLAdapter): # need to define all the 'sql' methods gt,lt etc.... def select(self,query,fields,attributes,count=False,snapshot=False): - try: - from pymongo.objectid import ObjectId - except ImportError: - from bson.objectid import ObjectId tablename, mongoqry_dict, mongofields_dict, \ mongosort_list, limitby_limit, limitby_skip = \ self._select(query,fields,attributes) + ctable = self.connection[tablename] if count: return {'count' : ctable.find( @@ -5150,19 +5184,21 @@ class MongoDBAdapter(NoSQLAdapter): mongoqry_dict, mongofields_dict, skip=limitby_skip, limit=limitby_limit, sort=mongosort_list, snapshot=snapshot) # pymongo cursor object - # DEBUG: print "mongo_list_dicts=%s" % mongo_list_dicts + rows = [] ### populate row in proper order colnames = [str(field) for field in fields] - for k,record in enumerate(mongo_list_dicts): + # for k,record in enumerate(mongo_list_dicts): + for record in mongo_list_dicts: row=[] for fullcolname in colnames: colname = fullcolname.split('.')[1] column = '_id' if colname=='id' else colname if column in record: - if column == '_id' and isinstance( - record[column],ObjectId): - value = int(str(record[column]),16) + # if column in ('_id', "id") and isinstance( + # record[column], self.ObjectId): + if isinstance(record[column], self.ObjectId): + value = int(str(record[column]), 16) elif column != '_id': value = record[column] else: @@ -5172,7 +5208,12 @@ class MongoDBAdapter(NoSQLAdapter): row.append(value) rows.append(row) processor = attributes.get('processor',self.parse) - return processor(rows,fields,colnames,False) + result = processor(rows,fields,colnames,False) + # we need to point .id to ._id for scaffolding actions + for row in result: + if hasattr(row, "_id"): + row.id = row._id + return result def INVERT(self,first): #print "in invert first=%s" % first @@ -5198,43 +5239,61 @@ class MongoDBAdapter(NoSQLAdapter): filter = self.expand(query) f_v = [] - modify = { '$set' : dict(((k.name,self.represent(v,k.type)) for k,v in fields)) } return modify,filter - #TODO implement update - #TODO implement set operator - #TODO implement find and modify - #todo implement complex update + # TODO implement set operator + # TODO implement find and modify + # TODO implement complex update + def update(self,tablename,query,fields,safe=None): if safe==None: safe=self.safe - #return amount of adjusted rows or zero, but no exceptions related not finding the result + #return amount of adjusted rows or zero, but no exceptions + # @ related not finding the result if not isinstance(query,Query): raise RuntimeError("Not implemented") amount = self.count(query,False) modify,filter = self.oupdate(tablename,query,fields) try: + result = self.connection[tablename].update(filter, + modify, multi=True, safe=safe) if safe: - return self.connection[tablename].update(filter,modify,multi=True,safe=safe).n + try: + # if result count is available fetch it + return result["n"] + except (KeyError, AttributeError, TypeError): + return amount else: - amount =self.count(query) - self.connection[tablename].update(filter,modify,multi=True,safe=safe) return amount - except: + except Exception, e: #TODO Reverse update query to verifiy that the query succeded - return 0 + raise RuntimeError("uncaught exception when updating rows: %s" % e) + """ + (NOTE: missing method for this docstring) An special update operator that enables the update of specific field return a dict """ - - #this function returns a dict with the where clause and update fields def _update(self,tablename,query,fields): return str(self.oupdate(tablename,query,fields)) + def delete(self, tablename, query, safe=None): + if safe is None: + safe = self.safe + amount = 0 + amount = self.count(query,False) + if not isinstance(query, Query): + raise RuntimeError("query type %s is not supported" % type(query)) + filter = self.expand(query) + self._delete(tablename, filter, safe=safe) + return amount + + def _delete(self, tablename, filter, safe=None): + return self.connection[tablename].remove(filter, safe=safe) + def bulk_insert(self, table, items): return [self.insert(table,item) for item in items] @@ -7451,8 +7510,8 @@ class SQLALL(object): def __str__(self): return ', '.join([str(field) for field in self._table]) - -class Reference(int): +# class Reference(int): +class Reference(long): def __allocate(self): if not self._record: From 532ddf1d9a60f1555c904bb7dd5c2a8077814066 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 1 Jan 2013 19:08:46 -0600 Subject: [PATCH 08/75] fixed issue 1249, thanks Alan --- VERSION | 2 +- applications/admin/controllers/default.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 6cb2c025..601850df 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.01.19.02.47 +Version 2.4.1-alpha.2+timestamp.2013.01.01.19.08.01 diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index 4004402a..ba67a9f5 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -1493,7 +1493,9 @@ def errors(): else: for item in request.vars: - if item[:7] == 'delete_': + # delete_all} rows doesn't contain any ticket + # Remove anything else as requested + if item[:7] == 'delete_' and (not item == "delete_all}"): os.unlink(apath('%s/errors/%s' % (app, item[7:]), r=request)) func = lambda p: os.stat(apath('%s/errors/%s' % (app, p), r=request)).st_mtime From 91be7eb3dccccf170f8b3fc26533f8a46f4517c0 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 1 Jan 2013 21:09:09 -0600 Subject: [PATCH 09/75] fixed opener in oauth2, thanks Ignacio Ocampo --- Makefile | 1 - VERSION | 2 +- gluon/contrib/login_methods/oauth20_account.py | 15 ++++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index ccb5f5c7..f38e1bc3 100644 --- a/Makefile +++ b/Makefile @@ -121,7 +121,6 @@ commit: push: hg push git push - git push mdipierro tag: git tag -l '$(S)' hg tag -l '$(S)' diff --git a/VERSION b/VERSION index 601850df..2f56e326 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.01.19.08.01 +Version 2.4.1-alpha.2+timestamp.2013.01.01.21.07.46 diff --git a/gluon/contrib/login_methods/oauth20_account.py b/gluon/contrib/login_methods/oauth20_account.py index 0ed3adb2..788f08d0 100644 --- a/gluon/contrib/login_methods/oauth20_account.py +++ b/gluon/contrib/login_methods/oauth20_account.py @@ -115,19 +115,20 @@ server for requests. It can be used for the optional"scope" parameters for Face uri += '?' + urlencode(r.get_vars) return uri + def __build_url_opener(self, uri): """ Build the url opener for managing HTTP Basic Athentication """ # Create an OpenerDirector with support # for Basic HTTP Authentication... - - auth_handler = urllib2.HTTPBasicAuthHandler() - auth_handler.add_password(None, - uri, - self.client_id, - self.client_secret) - opener = urllib2.build_opener(auth_handler) + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + password_mgr.add_password(realm=None, + uri=uri, + user=self.client_id, + passwd=self.client_secret) + handler = urllib2.HTTPBasicAuthHandler(password_mgr) + opener = urllib2.build_opener(handler) return opener def accessToken(self): From 828a4d7a60021b20bd9dfacf8235e9113156271b Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 2 Jan 2013 18:58:54 -0600 Subject: [PATCH 10/75] stopped error progragation in ajax, thanks Niphlod --- VERSION | 2 +- gluon/html.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 2f56e326..315a9b73 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.01.21.07.46 +Version 2.4.1-alpha.2+timestamp.2013.01.02.18.58.15 diff --git a/gluon/html.py b/gluon/html.py index e38c4d82..5b532b17 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -1478,7 +1478,7 @@ class A(DIV): (self['component'], self['target'] or '', d) self['_href'] = self['_href'] or '#null' elif self['callback']: - returnfalse = "var e = arguments[0] || window.event; e.cancelBubble=true; if (e.stopPropagation) e.stopPropagation(); e.preventDefault();" + returnfalse = "var e = arguments[0] || window.event; e.cancelBubble=true; if (e.stopPropagation) {e.stopPropagation(); e.stopImmediatePropagation(); e.preventDefault();}" if d: self['_onclick'] = "if(confirm(w2p_ajax_confirm_message||'Are you sure you want to delete this object?')){ajax('%s',[],'%s');%s};%s" % \ (self['callback'], self['target'] or '', d, returnfalse) From 54866ecef507f44a337554100280ea046e369838 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 3 Jan 2013 21:30:17 -0600 Subject: [PATCH 11/75] fixed auto patterns, thanks Denes --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 315a9b73..f42aeb29 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.02.18.58.15 +Version 2.4.1-alpha.2+timestamp.2013.01.03.21.29.36 diff --git a/gluon/dal.py b/gluon/dal.py index e5085787..e0bf0560 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7084,7 +7084,7 @@ def index(): patterns += auto_table(table,base=tag,depth=depth-1) return patterns - if patterns==DEFAULT: + if patterns == 'auto': patterns=[] for table in db.tables: if not table.startswith('auth_'): From b3e791b4d41a7c24bea6aca3c374fc0170c8fb65 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 5 Jan 2013 18:36:23 -0600 Subject: [PATCH 12/75] fixed geoPoint bug, thanks Paolo Valleri --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index f42aeb29..d64c6f8c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.03.21.29.36 +Version 2.4.1-alpha.2+timestamp.2013.01.05.18.35.40 diff --git a/gluon/dal.py b/gluon/dal.py index e0bf0560..31a2fc08 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -9736,7 +9736,7 @@ DAL.Table = Table # was necessary in gluon/globals.py session.connect # Geodal utils ################################################################################ -def geoPoint(*line): +def geoPoint(x,y): return "POINT (%f %f)" % (x,y) def geoLine(*line): From 46d5035437051f5c6392481e451e41da76bf8dba Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 5 Jan 2013 18:46:10 -0600 Subject: [PATCH 13/75] fixed dist type, thanks Paolo Valleri --- VERSION | 2 +- gluon/dal.py | 31 +++++++++++-------------------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/VERSION b/VERSION index d64c6f8c..2f09d917 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.05.18.35.40 +Version 2.4.1-alpha.2+timestamp.2013.01.05.18.45.27 diff --git a/gluon/dal.py b/gluon/dal.py index 31a2fc08..1034aeb4 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -2582,13 +2582,6 @@ class PostgreSQLAdapter(BaseAdapter): """ return 'ST_AsText(%s)' %(self.expand(first)) -# def ST_CONTAINED(self, first, second): -# """ -# non-standard function based on ST_Contains with parameters reversed -# http://postgis.org/docs/ST_Contains.html -# """ -# return 'ST_Contains(%s,%s)' % (self.expand(second, first.type), self.expand(first)) - def ST_CONTAINS(self, first, second): """ http://postgis.org/docs/ST_Contains.html @@ -8413,8 +8406,8 @@ class Expression(object): db = self.db return Expression(db, db._adapter.AS, self, alias, self.type) - # GIS functions - + # GIS expressions + def st_asgeojson(self, precision=15, options=0, version=1): return Expression(self.db, self.db._adapter.ST_ASGEOJSON, self, dict(precision=precision, options=options, @@ -8422,20 +8415,22 @@ class Expression(object): def st_astext(self): db = self.db - return Expression(db, db._adapter.ST_ASTEXT, self, type='string') + return Expression(db, db._adapter.ST_ASTEXT, self, 'string') - def st_contained(self, value): + def st_distance(self, other): db = self.db - return Query(db, db._adapter.ST_CONTAINS, value, self) + return Expression(db,db._adapter.ST_DISTANCE,self,other, 'double') + + def st_simplify(self, value): + db = self.db + return Expression(db, db._adapter.ST_SIMPLIFY, self, value, self.type) + + # GIS queries def st_contains(self, value): db = self.db return Query(db, db._adapter.ST_CONTAINS, self, value) - def st_distance(self, other): - db = self.db - return Expression(db,db._adapter.ST_DISTANCE,self,other,self.type) - def st_equals(self, value): db = self.db return Query(db, db._adapter.ST_EQUALS, self, value) @@ -8448,10 +8443,6 @@ class Expression(object): db = self.db return Query(db, db._adapter.ST_OVERLAPS, self, value) - def st_simplify(self, value): - db = self.db - return Expression(db, db._adapter.ST_SIMPLIFY, self, value) - def st_touches(self, value): db = self.db return Query(db, db._adapter.ST_TOUCHES, self, value) From c625be0a4a4ef54f0cfc14e72ffc62dd90340247 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 6 Jan 2013 09:28:01 -0600 Subject: [PATCH 14/75] fixed another geodal issue, thanks Paolo --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 2f09d917..5151471c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.05.18.45.27 +Version 2.4.1-alpha.2+timestamp.2013.01.06.09.27.15 diff --git a/gluon/dal.py b/gluon/dal.py index 1034aeb4..8957c893 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -8415,7 +8415,7 @@ class Expression(object): def st_astext(self): db = self.db - return Expression(db, db._adapter.ST_ASTEXT, self, 'string') + return Expression(db, db._adapter.ST_ASTEXT, self, type='string') def st_distance(self, other): db = self.db From 69d00a50a603dbd70d035ea66b3c65d83eb5ffc3 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 7 Jan 2013 10:34:31 -0600 Subject: [PATCH 15/75] support for motor and pulsar servers, thanks Niphlod --- VERSION | 2 +- anyserver.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 5151471c..11f9dcf6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.06.09.27.15 +Version 2.4.1-alpha.2+timestamp.2013.01.07.10.33.48 diff --git a/anyserver.py b/anyserver.py index 473cceaa..ebf556c8 100644 --- a/anyserver.py +++ b/anyserver.py @@ -163,6 +163,22 @@ class Servers: "tcp://127.0.0.1:9996") mongrel2_handler(app, conn, debug=False) + @staticmethod + def motor(app, address, **options): + #https://github.com/rpedroso/motor + import motor + app = motor.WSGIContainer(app) + http_server = motor.HTTPServer(app) + http_server.listen(address=address[0], port=address[1]) + #http_server.start(2) + motor.IOLoop.instance().start() + + @staticmethod + def pulsar(app, address, **options): + from pulsar.apps import wsgi + sys.argv = ['anyserver.py'] + s = wsgi.WSGIServer(callable=app, bind="%s:%d" % address) + s.start() def run(servername, ip, port, softcron=True, logging=False, profiler=None): if logging: From bd485d37c9e2912d1a13fb66d4aa25ebf9256b2a Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 08:49:18 -0600 Subject: [PATCH 16/75] fixed st_x and st_y, thanks Paolo Valleri --- VERSION | 2 +- gluon/dal.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 11f9dcf6..359d305c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.07.10.33.48 +Version 2.4.1-alpha.2+timestamp.2013.01.08.08.48.34 diff --git a/gluon/dal.py b/gluon/dal.py index 8957c893..6c0f7b71 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -2582,6 +2582,18 @@ class PostgreSQLAdapter(BaseAdapter): """ return 'ST_AsText(%s)' %(self.expand(first)) + def ST_X(self, first): + """ + http://postgis.org/docs/ST_X.html + """ + return 'ST_X(%s)' %(self.expand(first)) + + def ST_Y(self, first): + """ + http://postgis.org/docs/ST_Y.html + """ + return 'ST_Y(%s)' %(self.expand(first)) + def ST_CONTAINS(self, first, second): """ http://postgis.org/docs/ST_Contains.html @@ -8417,6 +8429,14 @@ class Expression(object): db = self.db return Expression(db, db._adapter.ST_ASTEXT, self, type='string') + def st_x(self): + db = self.db + return Expression(db, db._adapter.ST_X, self, type='string') + + def st_y(self): + db = self.db + return Expression(db, db._adapter.ST_Y, self, type='string') + def st_distance(self, other): db = self.db return Expression(db,db._adapter.ST_DISTANCE,self,other, 'double') From 30e32c07b94627ff36d14fd0cbccfaed8fa01f9b Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 08:58:30 -0600 Subject: [PATCH 17/75] fixed issue 1252, thanks Mark Weissen --- VERSION | 2 +- gluon/contrib/login_methods/openid_auth.py | 29 ++++++++++++---------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/VERSION b/VERSION index 359d305c..6efa1366 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.08.48.34 +Version 2.4.1-alpha.2+timestamp.2013.01.08.08.57.51 diff --git a/gluon/contrib/login_methods/openid_auth.py b/gluon/contrib/login_methods/openid_auth.py index 4e977030..0c763e81 100644 --- a/gluon/contrib/login_methods/openid_auth.py +++ b/gluon/contrib/login_methods/openid_auth.py @@ -129,18 +129,21 @@ class OpenIDAuth(object): def _define_alt_login_table(self): """ Define the OpenID login table. - Note: type is what I used for our project. We're going to support 'fackbook' and - 'plurk' alternate login methods. Otherwise it's always 'openid' and you + Note: oidtype is what I used for our project. + We're going to support 'fackbook' and + 'plurk' alternate login methods. + Otherwise it's always 'openid' and you may not need it. This should be easy to changed. (Just remove the field of "type" and remove the - "and db.alt_logins.type == type_" in _find_matched_openid function) + "and db.alt_logins.oidtype == type_" + in _find_matched_openid function) """ db = self.db table = db.define_table( self.table_alt_logins_name, Field('username', length=512, default=''), - Field('type', length=128, default='openid', readable=False), - Field('user', self.table_user, readable=False), + Field('oidtype', length=128, default='openid', readable=False), + Field('oiduser', self.table_user, readable=False), ) table.username.requires = IS_NOT_IN_DB(db, table.username) self.table_alt_logins = table @@ -213,7 +216,7 @@ class OpenIDAuth(object): # Get existed OpenID user user = db( - self.table_user.id == alt_login.user).select().first() + self.table_user.id == alt_login.oiduser).select().first() if user: if current.session.w2popenid: del(current.session.w2popenid) @@ -230,7 +233,7 @@ class OpenIDAuth(object): Get the matched OpenID for given """ query = ( - (db.alt_logins.username == oid) & (db.alt_logins.type == type_)) + (db.alt_logins.username == oid) & (db.alt_logins.oidtype == type_)) alt_login = db(query).select().first() # Get the OpenID record return alt_login @@ -239,7 +242,7 @@ class OpenIDAuth(object): Associate the user logged in with given OpenID """ # print "[DB] %s authenticated" % oid - self.db.alt_logins.insert(username=oid, user=user.id) + self.db.alt_logins.insert(username=oid, oiduser=user.id) def _form_with_notification(self): """ @@ -400,7 +403,7 @@ width: 400px; if 'delete_openid' in request.vars: self.remove_openid(request.vars.delete_openid) - query = self.db.alt_logins.user == self.auth.user.id + query = self.db.alt_logins.oiduser == self.auth.user.id alt_logins = self.db(query).select() l = [] for alt_login in alt_logins: @@ -529,7 +532,7 @@ class Web2pyStore(OpenIDStore): self.database.define_table(self.table_oid_nonces_name, Field('server_url', 'string', length=2047, required=True), - Field('timestamp', + Field('itimestamp', 'integer', required=True), Field('salt', 'string', length=40, required=True) @@ -591,12 +594,12 @@ class Web2pyStore(OpenIDStore): db = self.database if abs(timestamp - time.time()) > nonce.SKEW: return False - query = (db.oid_nonces.server_url == server_url) & (db.oid_nonces.timestamp == timestamp) & (db.oid_nonces.salt == salt) + query = (db.oid_nonces.server_url == server_url) & (db.oid_nonces.itimestamp == timestamp) & (db.oid_nonces.salt == salt) if db(query).count() > 0: return False else: db.oid_nonces.insert(server_url=server_url, - timestamp=timestamp, + itimestamp=timestamp, salt=salt) return True @@ -628,7 +631,7 @@ class Web2pyStore(OpenIDStore): """ db = self.database - query = (db.oid_nonces.timestamp < time.time() - nonce.SKEW) + query = (db.oid_nonces.itimestamp < time.time() - nonce.SKEW) return db(query).delete() def cleanupAssociations(self): From 88d2216780c11df2ef5280509d55c2490b2334a3 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 09:06:30 -0600 Subject: [PATCH 18/75] fixed issue 1254, change of scope for web2py-component-command and web2py-component-flash, thanks Paolo valleri --- VERSION | 2 +- applications/admin/static/js/web2py.js | 25 +++++++++++++---------- applications/examples/static/js/web2py.js | 25 +++++++++++++---------- applications/welcome/static/js/web2py.js | 25 +++++++++++++---------- 4 files changed, 43 insertions(+), 34 deletions(-) diff --git a/VERSION b/VERSION index 6efa1366..f926f3f5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.08.57.51 +Version 2.4.1-alpha.2+timestamp.2013.01.08.09.05.42 diff --git a/applications/admin/static/js/web2py.js b/applications/admin/static/js/web2py.js index 2c58fbf0..e3076795 100644 --- a/applications/admin/static/js/web2py.js +++ b/applications/admin/static/js/web2py.js @@ -44,12 +44,25 @@ function web2py_event_handlers() { doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; doc.on('click', "input[type='checkbox'].delete", function(){if(this.checked) if(!confirm(confirm_message)) this.checked=false;}); + doc.ajaxSuccess(function(e, xhr) { var redirect=xhr.getResponseHeader('web2py-redirect-location'); - if (redirect != null) { + var command=xhr.getResponseHeader('web2py-component-command'); + var flash=xhr.getResponseHeader('web2py-component-flash'); + if (redirect !== null) { window.location = redirect; }; + if(command !== null){ + eval(decodeURIComponent(command)); + } + if(flash) { + jQuery('.flash') + .html(decodeURIComponent(flash)) + .append('×') + .slideDown(); + } }); + doc.ajaxError(function(e, xhr, settings, exception) { doc.off('click', '.flash') switch(xhr.status){ @@ -98,8 +111,6 @@ function web2py_ajax_page(method, action, data, target) { 'complete':function(xhr,text){ var html=xhr.responseText; var content=xhr.getResponseHeader('web2py-component-content'); - var command=xhr.getResponseHeader('web2py-component-command'); - var flash=xhr.getResponseHeader('web2py-component-flash'); var t = jQuery('#'+target); if(content=='prepend') t.prepend(html); else if(content=='append') t.append(html); @@ -107,14 +118,6 @@ function web2py_ajax_page(method, action, data, target) { web2py_trap_form(action,target); web2py_trap_link(target); web2py_ajax_init('#'+target); - if(command) - eval(decodeURIComponent(command)); - if(flash) { - jQuery('.flash') - .html(decodeURIComponent(flash)) - .append('×') - .slideDown(); - } } }); } diff --git a/applications/examples/static/js/web2py.js b/applications/examples/static/js/web2py.js index 2c58fbf0..e3076795 100644 --- a/applications/examples/static/js/web2py.js +++ b/applications/examples/static/js/web2py.js @@ -44,12 +44,25 @@ function web2py_event_handlers() { doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; doc.on('click', "input[type='checkbox'].delete", function(){if(this.checked) if(!confirm(confirm_message)) this.checked=false;}); + doc.ajaxSuccess(function(e, xhr) { var redirect=xhr.getResponseHeader('web2py-redirect-location'); - if (redirect != null) { + var command=xhr.getResponseHeader('web2py-component-command'); + var flash=xhr.getResponseHeader('web2py-component-flash'); + if (redirect !== null) { window.location = redirect; }; + if(command !== null){ + eval(decodeURIComponent(command)); + } + if(flash) { + jQuery('.flash') + .html(decodeURIComponent(flash)) + .append('×') + .slideDown(); + } }); + doc.ajaxError(function(e, xhr, settings, exception) { doc.off('click', '.flash') switch(xhr.status){ @@ -98,8 +111,6 @@ function web2py_ajax_page(method, action, data, target) { 'complete':function(xhr,text){ var html=xhr.responseText; var content=xhr.getResponseHeader('web2py-component-content'); - var command=xhr.getResponseHeader('web2py-component-command'); - var flash=xhr.getResponseHeader('web2py-component-flash'); var t = jQuery('#'+target); if(content=='prepend') t.prepend(html); else if(content=='append') t.append(html); @@ -107,14 +118,6 @@ function web2py_ajax_page(method, action, data, target) { web2py_trap_form(action,target); web2py_trap_link(target); web2py_ajax_init('#'+target); - if(command) - eval(decodeURIComponent(command)); - if(flash) { - jQuery('.flash') - .html(decodeURIComponent(flash)) - .append('×') - .slideDown(); - } } }); } diff --git a/applications/welcome/static/js/web2py.js b/applications/welcome/static/js/web2py.js index 2c58fbf0..e3076795 100644 --- a/applications/welcome/static/js/web2py.js +++ b/applications/welcome/static/js/web2py.js @@ -44,12 +44,25 @@ function web2py_event_handlers() { doc.on('keyup', 'input.double, input.decimal', function(){this.value=this.value.reverse().replace(/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g,'').reverse();}); var confirm_message = (typeof w2p_ajax_confirm_message != 'undefined') ? w2p_ajax_confirm_message : "Are you sure you want to delete this object?"; doc.on('click', "input[type='checkbox'].delete", function(){if(this.checked) if(!confirm(confirm_message)) this.checked=false;}); + doc.ajaxSuccess(function(e, xhr) { var redirect=xhr.getResponseHeader('web2py-redirect-location'); - if (redirect != null) { + var command=xhr.getResponseHeader('web2py-component-command'); + var flash=xhr.getResponseHeader('web2py-component-flash'); + if (redirect !== null) { window.location = redirect; }; + if(command !== null){ + eval(decodeURIComponent(command)); + } + if(flash) { + jQuery('.flash') + .html(decodeURIComponent(flash)) + .append('×') + .slideDown(); + } }); + doc.ajaxError(function(e, xhr, settings, exception) { doc.off('click', '.flash') switch(xhr.status){ @@ -98,8 +111,6 @@ function web2py_ajax_page(method, action, data, target) { 'complete':function(xhr,text){ var html=xhr.responseText; var content=xhr.getResponseHeader('web2py-component-content'); - var command=xhr.getResponseHeader('web2py-component-command'); - var flash=xhr.getResponseHeader('web2py-component-flash'); var t = jQuery('#'+target); if(content=='prepend') t.prepend(html); else if(content=='append') t.append(html); @@ -107,14 +118,6 @@ function web2py_ajax_page(method, action, data, target) { web2py_trap_form(action,target); web2py_trap_link(target); web2py_ajax_init('#'+target); - if(command) - eval(decodeURIComponent(command)); - if(flash) { - jQuery('.flash') - .html(decodeURIComponent(flash)) - .append('×') - .slideDown(); - } } }); } From 424ea223349b8d5df0f51ccf32f10e83bc414ca9 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 09:10:34 -0600 Subject: [PATCH 19/75] Row.__copy__, fixed issue 1255 --- VERSION | 2 +- gluon/dal.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index f926f3f5..d51c6bc1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.09.05.42 +Version 2.4.1-alpha.2+timestamp.2013.01.08.09.09.49 diff --git a/gluon/dal.py b/gluon/dal.py index 6c0f7b71..6894c96e 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6521,6 +6521,8 @@ class Row(object): def __setitem__(self, key, value): setattr(self, str(key), value) + __copy__ = lambda self: Row(self) + __call__ = __getitem__ def get(self,key,default=None): From 2da5b2191fa26a77e982e922c195456089443ce0 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 09:17:42 -0600 Subject: [PATCH 20/75] fixed bug self._tables, thanks Mart --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index d51c6bc1..1044843e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.09.09.49 +Version 2.4.1-alpha.2+timestamp.2013.01.08.09.16.57 diff --git a/gluon/dal.py b/gluon/dal.py index 6894c96e..5cd4c9f7 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6899,6 +6899,7 @@ class DAL(object): self._migrated = [] self._LAZY_TABLES = {} self._lazy_tables = lazy_tables + self._tables = SQLCallableList() if not str(attempts).isdigit() or attempts < 0: attempts = 5 if uri: @@ -6951,7 +6952,6 @@ class DAL(object): migrate = fake_migrate = False adapter = self._adapter self._uri_hash = hashlib_md5(adapter.uri).hexdigest() - self._tables = SQLCallableList() self.check_reserved = check_reserved if self.check_reserved: from reserved_sql_keywords import ADAPTERS as RSK From ec21e81f19dd702b7caa14cc289761d0ed4f2697 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 09:23:18 -0600 Subject: [PATCH 21/75] json field in DAL, thanks Alan --- VERSION | 2 +- gluon/dal.py | 629 ++++++++++++++++++++++++------------------- gluon/serializers.py | 4 + gluon/sqlhtml.py | 24 +- gluon/validators.py | 35 +++ 5 files changed, 412 insertions(+), 282 deletions(-) diff --git a/VERSION b/VERSION index 1044843e..23f15a0d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.09.16.57 +Version 2.4.1-alpha.2+timestamp.2013.01.08.09.22.34 diff --git a/gluon/dal.py b/gluon/dal.py index 5cd4c9f7..fbc4c854 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -621,6 +621,7 @@ class BaseAdapter(ConnectionPool): 'boolean': 'CHAR(1)', 'string': 'CHAR(%(length)s)', 'text': 'TEXT', + 'json': 'TEXT', 'password': 'CHAR(%(length)s)', 'blob': 'BLOB', 'upload': 'CHAR(%(length)s)', @@ -1160,8 +1161,8 @@ class BaseAdapter(ConnectionPool): logfile.write('success!\n') def _insert(self, table, fields): - keys = ','.join(f.name for f,v in fields) - values = ','.join(self.expand(v,f.type) for f,v in fields) + keys = ','.join(f.name for f, v in fields) + values = ','.join(self.expand(v, f.type) for f, v in fields) return 'INSERT INTO %s(%s) VALUES (%s);' % (table, keys, values) def insert(self, table, fields): @@ -1225,7 +1226,7 @@ class BaseAdapter(ConnectionPool): self.expand('%'+second, 'string')) def CONTAINS(self, first, second): - if first.type in ('string', 'text'): + if first.type in ('string', 'text', 'json'): key = '%'+str(second).replace('%','%%')+'%' elif first.type.startswith('list:'): key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%' @@ -1719,7 +1720,7 @@ class BaseAdapter(ConnectionPool): obj = obj() if isinstance(fieldtype, SQLCustomType): value = fieldtype.encoder(obj) - if fieldtype.type in ('string','text'): + if fieldtype.type in ('string','text', 'json'): return self.adapt(value) return value if isinstance(obj, (Expression, Field)): @@ -1733,11 +1734,12 @@ class BaseAdapter(ConnectionPool): obj = map(str,obj) else: obj = map(int,obj) - if isinstance(obj, (list, tuple)): + # we don't want to bar_encode json objects + if isinstance(obj, (list, tuple)) and (not fieldtype == "json"): obj = bar_encode(obj) if obj is None: return 'NULL' - if obj == '' and not fieldtype[:2] in ['st', 'te', 'pa', 'up']: + if obj == '' and not fieldtype[:2] in ['st', 'te', 'js', 'pa', 'up']: return 'NULL' r = self.represent_exceptions(obj, fieldtype) if not r is None: @@ -1780,6 +1782,16 @@ class BaseAdapter(ConnectionPool): obj = obj.isoformat()[:10] else: obj = str(obj) + elif (fieldtype == 'json'): + if not isinstance(obj, basestring): + if have_serializers: + obj = serializers.json(obj) + else: + try: + import json as simplejson + except ImportError: + import gluon.contrib.simplejson as simplejson + obj = simplejson.dumps(items) if not isinstance(obj,bytes): obj = bytes(obj) try: @@ -1911,6 +1923,20 @@ class BaseAdapter(ConnectionPool): def parse_double(self, value, field_type): return float(value) + def parse_json(self, value, field_type): + if isinstance(value, basestring): + if isinstance(value, unicode): + value = value.encode('utf-8') + if have_serializers: + value = serializers.loads_json(value) + else: + try: + import json as simplejson + except ImportError: + import gluon.contrib.simplejson as simplejson + value = simplejson.loads(value) + return value + def build_parsemap(self): self.parsemap = { 'id':self.parse_id, @@ -1925,6 +1951,7 @@ class BaseAdapter(ConnectionPool): 'datetime':self.parse_datetime, 'blob':self.parse_blob, 'decimal':self.parse_decimal, + 'json':self.parse_json, 'list:integer':self.parse_list_integers, 'list:reference':self.parse_list_references, 'list:string':self.parse_list_strings, @@ -2307,6 +2334,7 @@ class MySQLAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'LONGTEXT', + 'json': 'LONGTEXT', 'password': 'VARCHAR(%(length)s)', 'blob': 'LONGBLOB', 'upload': 'VARCHAR(%(length)s)', @@ -2419,6 +2447,7 @@ class PostgreSQLAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'TEXT', + 'json': 'TEXT', 'password': 'VARCHAR(%(length)s)', 'blob': 'BYTEA', 'upload': 'VARCHAR(%(length)s)', @@ -2460,7 +2489,7 @@ class PostgreSQLAdapter(BaseAdapter): def ADD(self, first, second): t = first.type - if t in ('text','string','password','upload','blob'): + if t in ('text','string','password', 'json', 'upload','blob'): return '(%s || %s)' % (self.expand(first), self.expand(second, t)) else: return '(%s + %s)' % (self.expand(first), self.expand(second, t)) @@ -2561,7 +2590,7 @@ class PostgreSQLAdapter(BaseAdapter): self.expand('%'+second,'string')) def CONTAINS(self,first,second): - if first.type in ('string','text'): + if first.type in ('string','text', 'json'): key = '%'+str(second).replace('%','%%')+'%' elif first.type.startswith('list:'): key = '%|'+str(second).replace('|','||').replace('%','%%')+'|%' @@ -2666,6 +2695,7 @@ class NewPostgreSQLAdapter(PostgreSQLAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'TEXT', + 'json': 'TEXT', 'password': 'VARCHAR(%(length)s)', 'blob': 'BYTEA', 'upload': 'VARCHAR(%(length)s)', @@ -2765,6 +2795,7 @@ class OracleAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR2(%(length)s)', 'text': 'CLOB', + 'json': 'CLOB', 'password': 'VARCHAR2(%(length)s)', 'blob': 'CLOB', 'upload': 'VARCHAR2(%(length)s)', @@ -2935,6 +2966,7 @@ class MSSQLAdapter(BaseAdapter): 'boolean': 'BIT', 'string': 'VARCHAR(%(length)s)', 'text': 'TEXT', + 'json': 'TEXT', 'password': 'VARCHAR(%(length)s)', 'blob': 'IMAGE', 'upload': 'VARCHAR(%(length)s)', @@ -3148,6 +3180,7 @@ class MSSQL2Adapter(MSSQLAdapter): 'boolean': 'CHAR(1)', 'string': 'NVARCHAR(%(length)s)', 'text': 'NTEXT', + 'json': 'NTEXT', 'password': 'NVARCHAR(%(length)s)', 'blob': 'IMAGE', 'upload': 'NVARCHAR(%(length)s)', @@ -3172,7 +3205,7 @@ class MSSQL2Adapter(MSSQLAdapter): def represent(self, obj, fieldtype): value = BaseAdapter.represent(self, obj, fieldtype) - if fieldtype in ('string','text') and value[:1]=="'": + if fieldtype in ('string','text', 'json') and value[:1]=="'": value = 'N'+value return value @@ -3186,6 +3219,7 @@ class SybaseAdapter(MSSQLAdapter): 'boolean': 'BIT', 'string': 'CHAR VARYING(%(length)s)', 'text': 'TEXT', + 'json': 'TEXT', 'password': 'CHAR VARYING(%(length)s)', 'blob': 'IMAGE', 'upload': 'CHAR VARYING(%(length)s)', @@ -3280,6 +3314,7 @@ class FireBirdAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'BLOB SUB_TYPE 1', + 'json': 'BLOB SUB_TYPE 1', 'password': 'VARCHAR(%(length)s)', 'blob': 'BLOB SUB_TYPE 0', 'upload': 'VARCHAR(%(length)s)', @@ -3448,6 +3483,7 @@ class InformixAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'BLOB SUB_TYPE 1', + 'json': 'BLOB SUB_TYPE 1', 'password': 'VARCHAR(%(length)s)', 'blob': 'BLOB SUB_TYPE 0', 'upload': 'VARCHAR(%(length)s)', @@ -3575,6 +3611,7 @@ class DB2Adapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'CLOB', + 'json': 'CLOB', 'password': 'VARCHAR(%(length)s)', 'blob': 'BLOB', 'upload': 'VARCHAR(%(length)s)', @@ -3660,6 +3697,7 @@ class TeradataAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'CLOB', + 'json': 'CLOB', 'password': 'VARCHAR(%(length)s)', 'blob': 'BLOB', 'upload': 'VARCHAR(%(length)s)', @@ -3726,6 +3764,7 @@ class IngresAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'CLOB', + 'json': 'CLOB', 'password': 'VARCHAR(%(length)s)', ## Not sure what this contains utf8 or nvarchar. Or even bytes? 'blob': 'BLOB', 'upload': 'VARCHAR(%(length)s)', ## FIXME utf8 or nvarchar... or blob? what is this type? @@ -3828,6 +3867,7 @@ class IngresUnicodeAdapter(IngresAdapter): 'boolean': 'CHAR(1)', 'string': 'NVARCHAR(%(length)s)', 'text': 'NCLOB', + 'json': 'NCLOB', 'password': 'NVARCHAR(%(length)s)', ## Not sure what this contains utf8 or nvarchar. Or even bytes? 'blob': 'BLOB', 'upload': 'VARCHAR(%(length)s)', ## FIXME utf8 or nvarchar... or blob? what is this type? @@ -3858,6 +3898,7 @@ class SAPDBAdapter(BaseAdapter): 'boolean': 'CHAR(1)', 'string': 'VARCHAR(%(length)s)', 'text': 'LONG', + 'json': 'LONG', 'password': 'VARCHAR(%(length)s)', 'blob': 'LONG', 'upload': 'VARCHAR(%(length)s)', @@ -4159,7 +4200,7 @@ class NoSQLAdapter(BaseAdapter): if not isinstance(obj, (list, tuple)): obj = [obj] if obj == '' and not \ - (is_string and fieldtype[:2] in ['st','te','pa','up']): + (is_string and fieldtype[:2] in ['st','te', 'pa','up']): return None if not obj is None: if isinstance(obj, list) and not is_list: @@ -4202,6 +4243,17 @@ class NoSQLAdapter(BaseAdapter): obj = datetime.datetime(y, m, d, h, mi, s) elif fieldtype == 'blob': pass + elif fieldtype == 'json': + if isinstance(obj, basestring): + obj = self.to_unicode(obj) + if have_serializers: + obj = serializers.loads_json(obj) + else: + try: + import json as simplejson + except ImportError: + import gluon.contrib.simplejson as simplejson + obj = serializers.loads_json(obj) elif is_string and field_is_type('list:string'): return map(self.to_unicode,obj) elif is_list: @@ -4309,6 +4361,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter): 'boolean': gae.BooleanProperty, 'string': (lambda: gae.StringProperty(multiline=True)), 'text': gae.TextProperty, + 'json': gae.TextProperty, 'password': gae.StringProperty, 'blob': gae.BlobProperty, 'upload': gae.StringProperty, @@ -4388,7 +4441,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter): def expand(self,expression,field_type=None): if isinstance(expression,Field): - if expression.type in ('text','blob'): + if expression.type in ('text', 'blob', 'json'): raise SyntaxError('AppEngine does not index by: %s' % expression.type) return expression.name elif isinstance(expression, (Expression, Query)): @@ -4527,7 +4580,7 @@ class GoogleDatastoreAdapter(NoSQLAdapter): elif args_get('projection') == True: projection = [] for f in fields: - if f.type in ['text', 'blob']: + if f.type in ['text', 'blob', 'json']: raise SyntaxError( "text and blob field types not allowed in projection queries") else: @@ -4708,6 +4761,7 @@ class CouchDBAdapter(NoSQLAdapter): 'boolean': bool, 'string': str, 'text': str, + 'json': str, 'password': str, 'blob': str, 'upload': str, @@ -4907,6 +4961,7 @@ class MongoDBAdapter(NoSQLAdapter): 'boolean': bool, 'string': str, 'text': str, + 'json': str, 'password': str, 'blob': str, 'upload': str, @@ -4918,78 +4973,65 @@ class MongoDBAdapter(NoSQLAdapter): 'time': datetime.time, 'datetime': datetime.datetime, 'id': long, - 'mongo': unicode, # any Mongodb document (not implemented) 'reference': long, 'list:string': list, 'list:integer': list, 'list:reference': list, } + error_messages = {"javascript_needed": "This must yet be replaced" + + " with javascript in order to work."} + def __init__(self,db,uri='mongodb://127.0.0.1:5984/db', - pool_size=0,folder=None,db_codec ='UTF-8', + pool_size=0, folder=None, db_codec ='UTF-8', credential_decoder=IDENTITY, driver_args={}, adapter_args={}, do_connect=True): + self.db = db self.uri = uri if do_connect: self.find_driver(adapter_args) - - m=None - import random - try: - from pymongo.objectid import ObjectId - except ImportError: - from bson.objectid import ObjectId - try: - from bson.son import SON - except ImportError: - from pymongo.son import SON + from bson.objectid import ObjectId + from bson.son import SON + import pymongo.uri_parser + + m = pymongo.uri_parser.parse_uri(uri) self.SON = SON self.ObjectId = ObjectId self.random = random - try: - #Since version 2 - import pymongo.uri_parser - m = pymongo.uri_parser.parse_uri(uri) - except ImportError: - try: - #before version 2 of pymongo - import pymongo.connection - m = pymongo.connection._parse_uri(uri) - except ImportError: - raise ImportError("Uriparser for mongodb is not available") - except: - raise SyntaxError("This type of uri is not supported by the mongodb uri parser") self.dbengine = 'mongodb' self.folder = folder db['_lastsql'] = '' self.db_codec = 'UTF-8' self.pool_size = pool_size - #this is the minimum amount of replicates that it should wait for on insert/update + #this is the minimum amount of replicates that it should wait + # for on insert/update self.minimumreplication = adapter_args.get('minimumreplication',0) - #by default alle insert and selects are performand asynchronous, but now the default is - #synchronous, except when overruled by either this default or function parameter + # by default all inserts and selects are performand asynchronous, + # but now the default is + # synchronous, except when overruled by either this default or + # function parameter self.safe = adapter_args.get('safe',True) - if isinstance(m,tuple): m = {"database" : m[1]} if m.get('database')==None: raise SyntaxError("Database is required!") def connector(uri=self.uri,m=m): try: - return self.driver.Connection(uri)[m.get('database')] + # Connection() is deprecated + if hasattr(self.driver, "MongoClient"): + Connection = self.driver.MongoClient + else: + Connection = self.driver.Connection + return Connection(uri)[m.get('database')] except self.driver.errors.ConnectionFailure: inst = sys.exc_info()[1] - raise SyntaxError("The connection to " + uri + " could not be made") - except Exception: - inst = sys.exc_info()[1] - if inst == "cannot specify database without a username and password": - raise SyntaxError("You are probebly running version 1.1 of pymongo which contains a bug which requires authentication. Update your pymongo.") - else: - raise SyntaxError("This is not an official Mongodb uri (http://www.mongodb.org/display/DOCS/Connections) Error : %s" % inst) + raise SyntaxError("The connection to " + + uri + " could not be made") + self.reconnect(connector,cursor=False) def object_id(self, arg=None): @@ -5005,21 +5047,24 @@ class MongoDBAdapter(NoSQLAdapter): arg = int(arg) elif arg == "": arg = int("0x%sL" % \ - str("".join([self.random.choice("0123456789abcdef") for x in \ - range(24)])), 0) + "".join([self.random.choice("0123456789abcdef") \ + for x in range(24)]), 0) elif arg.isalnum(): if not arg.startswith("0x"): arg = "0x%s" % arg try: arg = int(arg, 0) except ValueError, e: - raise ValueError("invalid objectid argument string: %s" % e) + raise ValueError( + "invalid objectid argument string: %s" % e) else: - raise ValueError("invalid objectid argument string. requires an integer or base 16 value") + raise ValueError("Invalid objectid argument string. " + + "Requires an integer or base 16 value") elif isinstance(arg, self.ObjectId): return arg if not isinstance(arg, (int, long)): - raise TypeError("object_id argument must be of type ObjectId or an objectid representable integer") + raise TypeError("object_id argument must be of type " + + "ObjectId or an objectid representable integer") if arg == 0: hexvalue = "".zfill(24) else: @@ -5031,60 +5076,66 @@ class MongoDBAdapter(NoSQLAdapter): if fieldtype =='date': if value == None: return value - t = datetime.time(0, 0, 0)#this piece of data can be stripped off based on the fieldtype - return datetime.datetime.combine(value, t) #mongodb doesn't has a date object and so it must datetime, string or integer + # this piece of data can be stripped off based on the fieldtype + t = datetime.time(0, 0, 0) + # mongodb doesn't has a date object and so it must datetime, + # string or integer + return datetime.datetime.combine(value, t) elif fieldtype == 'time': if value == None: return value - d = datetime.date(2000, 1, 1) #this piece of data can be stripped of based on the fieldtype - return datetime.datetime.combine(d, value) #mongodb doesn't has a time object and so it must datetime, string or integer - elif fieldtype == 'list:string' or fieldtype == 'list:integer' or fieldtype == 'list:reference': - return value #raise SyntaxError("Not Supported") + # this piece of data can be stripped of based on the fieldtype + d = datetime.date(2000, 1, 1) + # mongodb doesn't has a time object and so it must datetime, + # string or integer + return datetime.datetime.combine(d, value) + elif fieldtype == 'list:string' or \ + fieldtype == 'list:integer' or \ + fieldtype == 'list:reference': + return value return value - #Safe determines whether a asynchronious request is done or a synchronious action is done - #For safety, we use by default synchronious requests - def insert(self,table,fields,safe=None): + # Safe determines whether a asynchronious request is done or a + # synchronious action is done + # For safety, we use by default synchronious requests + def insert(self, table, fields, safe=None): if safe==None: - safe=self.safe + safe = self.safe ctable = self.connection[table._tablename] values = dict() for k, v in fields: - # avoid writing "id" name reserved form Mongodb - if not k.name == "id": + if not k.name in ["id", "safe"]: fieldname = k.name fieldtype = table[k.name].type if ("reference" in fieldtype) or (fieldtype=="id"): values[fieldname] = self.object_id(v) else: values[fieldname] = self.represent(v, fieldtype) - ctable.insert(values,safe=safe) + ctable.insert(values, safe=safe) return int(str(values['_id']), 16) - def create_table(self, table, migrate=True, fake_migrate=False, polymodel=None, isCapped=False): + def create_table(self, table, migrate=True, fake_migrate=False, + polymodel=None, isCapped=False): if isCapped: raise RuntimeError("Not implemented") - else: - pass - def count(self,query,distinct=None,snapshot=True): + def count(self, query, distinct=None, snapshot=True): if distinct: raise RuntimeError("COUNT DISTINCT not supported") if not isinstance(query,Query): raise SyntaxError("Not Supported") tablename = self.get_table(query) - return int(self.select(query,[self.db[tablename]._id],{},count=True,snapshot=snapshot)['count']) - #Maybe it would be faster if we just implemented the pymongo .count() function which is probably quicker? - # therefor call __select() connection[table].find(query).count() Since this will probably reduce the return set? + return int(self.select(query,[self.db[tablename]._id], {}, + count=True,snapshot=snapshot)['count']) + # Maybe it would be faster if we just implemented the pymongo + # .count() function which is probably quicker? + # therefor call __select() connection[table].find(query).count() + # Since this will probably reduce the return set? def expand(self, expression, field_type=None): - #if isinstance(expression,Field): - # if expression.type=='id': - # return {_id}" - result = None if isinstance(expression, Query): # any query using 'id':= - # set name as _id (as per pymongo/mongodb primary key) + # set name as _id (as per pymongo/mongodb primary key) # convert second arg to an objectid field # (if its not already) # if second arg is 0 convert to objectid @@ -5101,7 +5152,7 @@ class MongoDBAdapter(NoSQLAdapter): result = "_id" else: result = expression.name - #return expression + elif isinstance(expression, (Expression, Query)): if not expression.second is None: result = expression.op(expression.first, expression.second) @@ -5114,15 +5165,17 @@ class MongoDBAdapter(NoSQLAdapter): elif field_type: result = str(self.represent(expression,field_type)) elif isinstance(expression,(list,tuple)): - result = ','.join(self.represent(item,field_type) for item in expression) + result = ','.join(self.represent(item,field_type) for + item in expression) else: result = expression return result - def _select(self,query,fields,attributes): + def _select(self, query, fields, attributes): if 'for_update' in attributes: logging.warn('mongodb does not support for_update') - for key in set(attributes.keys())-set(('limitby','orderby','for_update')): + for key in set(attributes.keys())-set(('limitby', + 'orderby','for_update')): if attributes[key]!=None: logging.warn('select attribute not implemented: %s' % key) @@ -5132,18 +5185,17 @@ class MongoDBAdapter(NoSQLAdapter): # try an orderby attribute orderby = attributes.get('orderby', False) limitby = attributes.get('limitby', False) - #distinct = attributes.get('distinct', False) + # distinct = attributes.get('distinct', False) if orderby: - #print "in if orderby %s" % orderby if isinstance(orderby, (list, tuple)): orderby = xorify(orderby) # !!!! need to add 'random' for f in self.expand(orderby).split(','): if f.startswith('-'): - mongosort_list.append((f[1:],-1)) + mongosort_list.append((f[1:], -1)) else: - mongosort_list.append((f,1)) + mongosort_list.append((f, 1)) if limitby: limitby_skip, limitby_limit = limitby @@ -5153,7 +5205,7 @@ class MongoDBAdapter(NoSQLAdapter): mongofields_dict = self.SON() mongoqry_dict = {} for item in fields: - if isinstance(item,SQLALL): + if isinstance(item, SQLALL): new_fields += item._table else: new_fields.append(item) @@ -5163,64 +5215,73 @@ class MongoDBAdapter(NoSQLAdapter): elif len(fields) != 0: tablename = fields[0].tablename else: - raise SyntaxError("The table name could not be found in the query nor from the select statement.") + raise SyntaxError("The table name could not be found in " + + "the query nor from the select statement.") + mongoqry_dict = self.expand(query) fields = fields or self.db[tablename] for field in fields: mongofields_dict[field.name] = 1 - return tablename, mongoqry_dict, mongofields_dict, \ - mongosort_list, limitby_limit, limitby_skip - # need to define all the 'sql' methods gt,lt etc.... + return tablename, mongoqry_dict, mongofields_dict, mongosort_list, \ + limitby_limit, limitby_skip - def select(self,query,fields,attributes,count=False,snapshot=False): - tablename, mongoqry_dict, mongofields_dict, \ - mongosort_list, limitby_limit, limitby_skip = \ - self._select(query,fields,attributes) + def select(self, query, fields, attributes, count=False, + snapshot=False): + # TODO: support joins + tablename, mongoqry_dict, mongofields_dict, mongosort_list, \ + limitby_limit, limitby_skip = self._select(query, fields, attributes) ctable = self.connection[tablename] + if count: return {'count' : ctable.find( mongoqry_dict, mongofields_dict, skip=limitby_skip, limit=limitby_limit, sort=mongosort_list, snapshot=snapshot).count()} else: - mongo_list_dicts = ctable.find( - mongoqry_dict, mongofields_dict, - skip=limitby_skip, limit=limitby_limit, - sort=mongosort_list, snapshot=snapshot) # pymongo cursor object - + # pymongo cursor object + mongo_list_dicts = ctable.find(mongoqry_dict, + mongofields_dict, skip=limitby_skip, + limit=limitby_limit, sort=mongosort_list, + snapshot=snapshot) rows = [] - ### populate row in proper order - colnames = [str(field) for field in fields] - # for k,record in enumerate(mongo_list_dicts): + # populate row in proper order + # Here we replace ._id with .id to follow the standard naming + colnames = [] + newnames = [] + for field in fields: + colname = str(field) + colnames.append(colname) + tablename, fieldname = colname.split(".") + if fieldname == "_id": + # Mongodb reserved uuid key + field.name = "id" + newnames.append(".".join((tablename, field.name))) + for record in mongo_list_dicts: row=[] - for fullcolname in colnames: - colname = fullcolname.split('.')[1] - column = '_id' if colname=='id' else colname - if column in record: - # if column in ('_id', "id") and isinstance( - # record[column], self.ObjectId): - if isinstance(record[column], self.ObjectId): - value = int(str(record[column]), 16) - elif column != '_id': - value = record[column] + for colname in colnames: + tablename, fieldname = colname.split(".") + # switch to Mongo _id uuids for retrieving + # record id's + if fieldname == "id": fieldname = "_id" + if fieldname in record: + if isinstance(record[fieldname], + self.ObjectId): + value = int(str(record[fieldname]), 16) else: - value = None + value = record[fieldname] else: value = None row.append(value) rows.append(row) - processor = attributes.get('processor',self.parse) - result = processor(rows,fields,colnames,False) - # we need to point .id to ._id for scaffolding actions - for row in result: - if hasattr(row, "_id"): - row.id = row._id + processor = attributes.get('processor', self.parse) + result = processor(rows, fields, newnames, False) return result - def INVERT(self,first): + + def INVERT(self, first): #print "in invert first=%s" % first return '-%s' % self.expand(first) @@ -5229,37 +5290,31 @@ class MongoDBAdapter(NoSQLAdapter): ctable.drop() - def truncate(self,table,mode,safe=None): - if safe==None: + def truncate(self, table, mode, safe=None): + if safe == None: safe=self.safe ctable = self.connection[table._tablename] ctable.remove(None, safe=True) - #the update function should return a string - def oupdate(self,tablename,query,fields): - if not isinstance(query,Query): + def oupdate(self, tablename, query, fields): + if not isinstance(query, Query): raise SyntaxError("Not Supported") filter = None if query: filter = self.expand(query) - f_v = [] + modify = {'$set': dict((k.name, self.represent(v, k.type)) for + k, v in fields)} + return modify, filter - modify = { '$set' : dict(((k.name,self.represent(v,k.type)) for k,v in fields)) } - return modify,filter - - # TODO implement set operator - # TODO implement find and modify - # TODO implement complex update - - def update(self,tablename,query,fields,safe=None): - if safe==None: - safe=self.safe - #return amount of adjusted rows or zero, but no exceptions + def update(self, tablename, query, fields, safe=None): + if safe == None: + safe = self.safe + # return amount of adjusted rows or zero, but no exceptions # @ related not finding the result - if not isinstance(query,Query): + if not isinstance(query, Query): raise RuntimeError("Not implemented") - amount = self.count(query,False) - modify,filter = self.oupdate(tablename,query,fields) + amount = self.count(query, False) + modify, filter = self.oupdate(tablename, query, fields) try: result = self.connection[tablename].update(filter, modify, multi=True, safe=safe) @@ -5272,26 +5327,21 @@ class MongoDBAdapter(NoSQLAdapter): else: return amount except Exception, e: - #TODO Reverse update query to verifiy that the query succeded + # TODO Reverse update query to verifiy that the query succeded raise RuntimeError("uncaught exception when updating rows: %s" % e) - """ - (NOTE: missing method for this docstring) - An special update operator that enables the update of specific field - return a dict - """ - #this function returns a dict with the where clause and update fields def _update(self,tablename,query,fields): - return str(self.oupdate(tablename,query,fields)) + return str(self.oupdate(tablename, query, fields)) def delete(self, tablename, query, safe=None): if safe is None: safe = self.safe amount = 0 - amount = self.count(query,False) + amount = self.count(query, False) if not isinstance(query, Query): - raise RuntimeError("query type %s is not supported" % type(query)) + raise RuntimeError("query type %s is not supported" % \ + type(query)) filter = self.expand(query) self._delete(tablename, filter, safe=safe) return amount @@ -5302,7 +5352,7 @@ class MongoDBAdapter(NoSQLAdapter): def bulk_insert(self, table, items): return [self.insert(table,item) for item in items] - #TODO This will probably not work:( + # TODO This will probably not work:( def NOT(self, first): result = {} result["$not"] = self.expand(first) @@ -5315,7 +5365,7 @@ class MongoDBAdapter(NoSQLAdapter): return f def OR(self,first,second): - # pymongo expects: .find( {'$or' : [{'name':'1'}, {'name':'2'}] } ) + # pymongo expects: .find({'$or': [{'name':'1'}, {'name':'2'}]}) result = {} f = self.expand(first) s = self.expand(second) @@ -5332,9 +5382,6 @@ class MongoDBAdapter(NoSQLAdapter): def EQ(self,first,second): result = {} - #if second is None: - #return '(%s == null)' % self.expand(first) - #return '(%s == %s)' % (self.expand(first),self.expand(second,first.type)) result[self.expand(first)] = self.expand(second) return result @@ -5370,84 +5417,101 @@ class MongoDBAdapter(NoSQLAdapter): return result def ADD(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") - return '%s + %s' % (self.expand(first), self.expand(second, first.type)) + raise NotImplementedError(self.error_messages["javascript_needed"]) + return '%s + %s' % (self.expand(first), + self.expand(second, first.type)) def SUB(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") - return '(%s - %s)' % (self.expand(first), self.expand(second, first.type)) + raise NotImplementedError(self.error_messages["javascript_needed"]) + return '(%s - %s)' % (self.expand(first), + self.expand(second, first.type)) def MUL(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") - return '(%s * %s)' % (self.expand(first), self.expand(second, first.type)) + raise NotImplementedError(self.error_messages["javascript_needed"]) + return '(%s * %s)' % (self.expand(first), + self.expand(second, first.type)) def DIV(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") - return '(%s / %s)' % (self.expand(first), self.expand(second, first.type)) + raise NotImplementedError(self.error_messages["javascript_needed"]) + return '(%s / %s)' % (self.expand(first), + self.expand(second, first.type)) def MOD(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") - return '(%s %% %s)' % (self.expand(first), self.expand(second, first.type)) + raise NotImplementedError(self.error_messages["javascript_needed"]) + return '(%s %% %s)' % (self.expand(first), + self.expand(second, first.type)) def AS(self, first, second): - raise NotImplementedError("This must yet be replaced with javascript in order to accomplish this. Sorry") + raise NotImplementedError(self.error_messages["javascript_needed"]) return '%s AS %s' % (self.expand(first), second) - #We could implement an option that simulates a full featured SQL database. But I think the option should be set explicit or implemented as another library. + # We could implement an option that simulates a full featured SQL + # database. But I think the option should be set explicit or + # implemented as another library. def ON(self, first, second): - raise NotImplementedError("This is not possible in NoSQL, but can be simulated with a wrapper.") + raise NotImplementedError("This is not possible in NoSQL" + + " but can be simulated with a wrapper.") return '%s ON %s' % (self.expand(first), self.expand(second)) - # # BLOW ARE TWO IMPLEMENTATIONS OF THE SAME FUNCITONS # WHICH ONE IS BEST? - # def COMMA(self, first, second): return '%s, %s' % (self.expand(first), self.expand(second)) def LIKE(self, first, second): #escaping regex operators? - return {self.expand(first) : ('%s' % self.expand(second, 'string').replace('%','/'))} + return {self.expand(first): ('%s' % \ + self.expand(second, 'string').replace('%','/'))} def STARTSWITH(self, first, second): #escaping regex operators? - return {self.expand(first) : ('/^%s/' % self.expand(second, 'string'))} + return {self.expand(first): ('/^%s/' % \ + self.expand(second, 'string'))} def ENDSWITH(self, first, second): #escaping regex operators? - return {self.expand(first) : ('/%s^/' % self.expand(second, 'string'))} + return {self.expand(first): ('/%s^/' % \ + self.expand(second, 'string'))} def CONTAINS(self, first, second): - #There is a technical difference, but mongodb doesn't support that, but the result will be the same - return {self.expand(first) : ('/%s/' % self.expand(second, 'string'))} + #There is a technical difference, but mongodb doesn't support + # that, but the result will be the same + return {self.expand(first) : ('/%s/' % \ + self.expand(second, 'string'))} def LIKE(self, first, second): import re - return {self.expand(first) : {'$regex' : re.escape(self.expand(second, 'string')).replace('%','.*')}} + return {self.expand(first): {'$regex': \ + re.escape(self.expand(second, + 'string')).replace('%','.*')}} #TODO verify full compatibilty with official SQL Like operator def STARTSWITH(self, first, second): #TODO Solve almost the same problem as with endswith import re - return {self.expand(first) : {'$regex' : '^' + re.escape(self.expand(second, 'string'))}} + return {self.expand(first): {'$regex' : '^' + + re.escape(self.expand(second, + 'string'))}} #TODO verify full compatibilty with official SQL Like operator def ENDSWITH(self, first, second): #escaping regex operators? - #TODO if searched for a name like zsa_corbitt and the function is endswith('a') then this is also returned. Aldo it end with a t + #TODO if searched for a name like zsa_corbitt and the function + # is endswith('a') then this is also returned. + # Aldo it end with a t import re - return {self.expand(first) : {'$regex' : re.escape(self.expand(second, 'string')) + '$'}} + return {self.expand(first): {'$regex': \ + re.escape(self.expand(second, 'string')) + '$'}} #TODO verify full compatibilty with official oracle contains operator def CONTAINS(self, first, second): - #There is a technical difference, but mongodb doesn't support that, but the result will be the same + #There is a technical difference, but mongodb doesn't support + # that, but the result will be the same #TODO contains operators need to be transformed to Regex - return {self.expand(first) : {' $regex' : ".*" + re.escape(self.expand(second, 'string')) + ".*"}} + return {self.expand(first) : {' $regex': \ + ".*" + re.escape(self.expand(second, 'string')) + ".*"}} - # - # END REDUNDANCY - # class IMAPAdapter(NoSQLAdapter): drivers = ('imaplib',) @@ -5880,17 +5944,26 @@ class IMAPAdapter(NoSQLAdapter): def create_table(self, *args, **kwargs): # not implemented - LOGGER.debug("Create table feature is not implemented for %s" % type(self)) + # but required by DAL + pass - def _select(self,query,fields,attributes): + def _select(self, query, fields, attributes): + if use_common_filters(query): + query = self.common_filter(query, [self.get_query_mailbox(query),]) + return str(query) + + def select(self,query,fields,attributes): """ Search and Fetch records and return web2py rows """ + # tablename, imapqry_array , fieldnames = self._select(query,fields,attributes) + ######################################## + ############# Start new .select() ###### + # move this statement elsewhere (upper-level) if use_common_filters(query): query = self.common_filter(query, [self.get_query_mailbox(query),]) - # move this statement elsewhere (upper-level) import email import email.header decode_header = email.header.decode_header @@ -5898,68 +5971,62 @@ class IMAPAdapter(NoSQLAdapter): # convert results to a dictionary tablename = None fetch_results = list() - if isinstance(query, (Expression, Query)): + if isinstance(query, Query): tablename = self.get_table(query) mailbox = self.connection.mailbox_names.get(tablename, None) - if isinstance(query, Expression): - pass - elif isinstance(query, Query): - if mailbox is not None: - # select with readonly - selected = self.connection.select(mailbox, True) - self.mailbox_size = int(selected[1][0]) - search_query = "(%s)" % str(query).strip() - search_result = self.connection.uid("search", None, search_query) - # Normal IMAP response OK is assumed (change this) - if search_result[0] == "OK": - # For "light" remote server responses just get the first - # ten records (change for non-experimental implementation) - # However, light responses are not guaranteed with this - # approach, just fewer messages. - # TODO: change limitby single to 2-tuple argument - limitby = attributes.get('limitby', None) - messages_set = search_result[1][0].split() - # descending order - messages_set.reverse() - if limitby is not None: - # TODO: asc/desc attributes - messages_set = messages_set[int(limitby[0]):int(limitby[1])] - # Partial fetches are not used since the email - # library does not seem to support it (it converts - # partial messages to mangled message instances) - imap_fields = "(RFC822)" - if len(messages_set) > 0: - # create fetch results object list - # fetch each remote message and store it in memmory - # (change to multi-fetch command syntax for faster - # transactions) - for uid in messages_set: - # fetch the RFC822 message body - typ, data = self.connection.uid("fetch", uid, imap_fields) - if typ == "OK": - fr = {"message": int(data[0][0].split()[0]), - "uid": int(uid), - "email": email.message_from_string(data[0][1]), - "raw_message": data[0][1] - } - fr["multipart"] = fr["email"].is_multipart() - # fetch flags for the message - ftyp, fdata = self.connection.uid("fetch", uid, "(FLAGS)") - if ftyp == "OK": - fr["flags"] = self.driver.ParseFlags(fdata[0]) - fetch_results.append(fr) - else: - # error retrieving the flags for this message - pass + if mailbox is not None: + # select with readonly + selected = self.connection.select(mailbox, True) + self.mailbox_size = int(selected[1][0]) + search_query = "(%s)" % str(query).strip() + search_result = self.connection.uid("search", None, search_query) + # Normal IMAP response OK is assumed (change this) + if search_result[0] == "OK": + # For "light" remote server responses just get the first + # ten records (change for non-experimental implementation) + # However, light responses are not guaranteed with this + # approach, just fewer messages. + # TODO: change limitby single to 2-tuple argument + limitby = attributes.get('limitby', None) + messages_set = search_result[1][0].split() + # descending order + messages_set.reverse() + if limitby is not None: + # TODO: asc/desc attributes + messages_set = messages_set[int(limitby[0]):int(limitby[1])] + # Partial fetches are not used since the email + # library does not seem to support it (it converts + # partial messages to mangled message instances) + imap_fields = "(RFC822)" + if len(messages_set) > 0: + # create fetch results object list + # fetch each remote message and store it in memmory + # (change to multi-fetch command syntax for faster + # transactions) + for uid in messages_set: + # fetch the RFC822 message body + typ, data = self.connection.uid("fetch", uid, imap_fields) + if typ == "OK": + fr = {"message": int(data[0][0].split()[0]), + "uid": int(uid), + "email": email.message_from_string(data[0][1]), + "raw_message": data[0][1]} + fr["multipart"] = fr["email"].is_multipart() + # fetch flags for the message + ftyp, fdata = self.connection.uid("fetch", uid, "(FLAGS)") + if ftyp == "OK": + fr["flags"] = self.driver.ParseFlags(fdata[0]) + fetch_results.append(fr) else: - # error retrieving the message body + # error retrieving the flags for this message pass - - elif isinstance(query, basestring): - # not implemented - pass + else: + # error retrieving the message body + pass + elif isinstance(query, (Expression, basestring)): + raise NotImplementedError() else: - pass + raise TypeError("Unexpected query type") imapqry_dict = {} imapfields_dict = {} @@ -6047,6 +6114,7 @@ class IMAPAdapter(NoSQLAdapter): item_dict["%s.answered" % tablename] = "\\Answered" in flags if "%s.mime" % tablename in fieldnames: item_dict["%s.mime" % tablename] = message.get_content_type() + # Here goes the whole RFC822 body as an email instance # for controller side custom processing # The message is stored as a raw string @@ -6054,6 +6122,7 @@ class IMAPAdapter(NoSQLAdapter): # returns a Message object for enhanced object processing if "%s.email" % tablename in fieldnames: item_dict["%s.email" % tablename] = self.encode_text(raw_message, charset) + # Size measure as suggested in a Velocity Reviews post # by Tim Williams: "how to get size of email attachment" # Note: len() and server RFC822.SIZE reports doesn't match @@ -6070,11 +6139,9 @@ class IMAPAdapter(NoSQLAdapter): if "%s.size" % tablename in fieldnames: if part is not None: size += len(str(part)) - item_dict["%s.content" % tablename] = bar_encode(content) item_dict["%s.attachments" % tablename] = bar_encode(attachments) item_dict["%s.size" % tablename] = size - imapqry_list.append(item_dict) # extra object mapping for the sake of rows object @@ -6085,23 +6152,22 @@ class IMAPAdapter(NoSQLAdapter): imapqry_array_item.append(item_dict[fieldname]) imapqry_array.append(imapqry_array_item) - return tablename, imapqry_array, fieldnames + # return tablename, imapqry_array, fieldnames + ############# End new .select() ######## + ######################################## - def select(self,query,fields,attributes): - tablename, imapqry_array , fieldnames = self._select(query,fields,attributes) # parse result and return a rows object colnames = fieldnames processor = attributes.get('processor',self.parse) return processor(imapqry_array, fields, colnames) - def update(self, tablename, query, fields): + def _update(self, tablename, query, fields, commit=False): + # TODO: the adapter should implement an .expand method + commands = list() if use_common_filters(query): query = self.common_filter(query, [tablename,]) - mark = [] unmark = [] - rowcount = 0 - query = str(query) if query: for item in fields: field = item[0] @@ -6114,26 +6180,33 @@ class IMAPAdapter(NoSQLAdapter): mark.append(flag) else: unmark.append(flag) - result, data = self.connection.select( self.connection.mailbox_names[tablename]) string_query = "(%s)" % query result, data = self.connection.search(None, string_query) store_list = [item.strip() for item in data[0].split() if item.strip().isdigit()] - # change marked flags + # build commands for marked flags for number in store_list: result = None if len(mark) > 0: - result, data = self.connection.store( - number, "+FLAGS", "(%s)" % " ".join(mark)) + commands.append((number, "+FLAGS", "(%s)" % " ".join(mark))) if len(unmark) > 0: - result, data = self.connection.store( - number, "-FLAGS", "(%s)" % " ".join(unmark)) - if result == "OK": - rowcount += 1 + commands.append((number, "-FLAGS", "(%s)" % " ".join(unmark))) + return commands + + def update(self, tablename, query, fields): + rowcount = 0 + commands = self._update(tablename, query, fields) + for command in commands: + result, data = self.connection.store(*command) + if result == "OK": + rowcount += 1 return rowcount + def _count(self, query, distinct=None): + raise NotImplementedError() + def count(self,query,distinct=None): counter = 0 tablename = self.get_query_mailbox(query) @@ -6409,12 +6482,10 @@ def sqlhtml_validators(field): return r._format(row) else: return id - if field_type == 'string': - requires.append(validators.IS_LENGTH(field_length)) - elif field_type == 'text': - requires.append(validators.IS_LENGTH(field_length)) - elif field_type == 'password': + if field_type in (('string', 'text', 'password')): requires.append(validators.IS_LENGTH(field_length)) + elif field_type == 'json': + requires.append(validators.IS_EMPTY_OR(validators.IS_JSON())) elif field_type == 'double' or field_type == 'float': requires.append(validators.IS_FLOAT_IN_RANGE(-1e100, 1e100)) elif field_type in ('integer','bigint'): @@ -6696,7 +6767,7 @@ def smart_query(fields,text): value = constants[item[1:]] else: value = item - if field.type in ('text','string'): + if field.type in ('text', 'string', 'json'): if op == '=': op = 'like' if op == '=': new_query = field==value elif op == '<': new_query = field :db_codec: string encoding of the database (default: 'UTF-8') :check_reserved: list of adapters to check tablenames and column names - against sql reserved keywords. (Default None) + against sql/nosql reserved keywords. (Default None) * 'common' List of sql keywords that are common to all database types such as "SELECT, INSERT". (recommended) @@ -7000,7 +7071,7 @@ class DAL(object): for backend in self.check_reserved: if name.upper() in self.RSK[backend]: raise SyntaxError( - 'invalid table/column name "%s" is a "%s" reserved SQL keyword' % (name, backend.upper())) + 'invalid table/column name "%s" is a "%s" reserved SQL/NOSQL keyword' % (name, backend.upper())) def parse_as_rest(self,patterns,args,vars,queries=None,nested_select=True): """ @@ -7696,7 +7767,7 @@ class Table(object): field.tablename = field._tablename = tablename field.table = field._table = self field.db = field._db = db - if db and not field.type in ('text','blob') and \ + if db and not field.type in ('text', 'blob', 'json') and \ db._adapter.maxcharlength < field.length: field.length = db._adapter.maxcharlength self.ALL = SQLALL(self) @@ -8394,13 +8465,13 @@ class Expression(object): def startswith(self, value): db = self.db - if not self.type in ('string', 'text'): + if not self.type in ('string', 'text', 'json'): raise SyntaxError("startswith used with incompatible field type") return Query(db, db._adapter.STARTSWITH, self, value) def endswith(self, value): db = self.db - if not self.type in ('string', 'text'): + if not self.type in ('string', 'text', 'json'): raise SyntaxError("endswith used with incompatible field type") return Query(db, db._adapter.ENDSWITH, self, value) @@ -8412,7 +8483,7 @@ class Expression(object): return self.contains('') else: return reduce(all and AND or OR,subqueries) - if not self.type in ('string', 'text') and not self.type.startswith('list:'): + if not self.type in ('string', 'text', 'json') and not self.type.startswith('list:'): raise SyntaxError("contains used with incompatible field type") return Query(db, db._adapter.CONTAINS, self, value) @@ -9515,7 +9586,9 @@ class Rows(object): items = [[inner_loop(record, col) for col in self.colnames] for record in self] if have_serializers: - return serializers.json(items,default=default or serializers.custom_json) + return serializers.json(items, + default=default or + serializers.custom_json) else: try: import json as simplejson diff --git a/gluon/serializers.py b/gluon/serializers.py index 3cdb91fc..e29061a1 100644 --- a/gluon/serializers.py +++ b/gluon/serializers.py @@ -20,6 +20,10 @@ except ImportError: import contrib.simplejson as json_parser # fallback to pure-Python module +def loads_json(o): + # deserialize a json string + return json_parser.loads(o) + def custom_json(o): if hasattr(o, 'custom_json') and callable(o.custom_json): return o.custom_json() diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index f8228046..36082b34 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -30,6 +30,7 @@ from utils import md5_hash from validators import IS_EMPTY_OR, IS_NOT_EMPTY, IS_LIST_OF, IS_DATE, \ IS_DATETIME, IS_INT_IN_RANGE, IS_FLOAT_IN_RANGE, IS_STRONG +import serializers import datetime import urllib import re @@ -39,6 +40,9 @@ import inspect import settings is_gae = settings.global_settings.web2py_runtime_gae + + + table_field = re.compile('[\w_]+\.[\w_]+') widget_class = re.compile('^\w*') @@ -164,11 +168,9 @@ class TimeWidget(StringWidget): class DateWidget(StringWidget): _class = 'date' - class DatetimeWidget(StringWidget): _class = 'datetime' - class TextWidget(FormWidget): _class = 'text' @@ -184,6 +186,22 @@ class TextWidget(FormWidget): attr = cls._attributes(field, default, **attributes) return TEXTAREA(**attr) +class JSONWidget(FormWidget): + _class = 'json' + + @classmethod + def widget(cls, field, value, **attributes): + """ + generates a TEXTAREA for JSON notation. + + see also: :meth:`FormWidget.widget` + """ + if not isinstance(value, basestring): + if value is not None: + value = serializers.json(value) + default = dict(value=value) + attr = cls._attributes(field, default, **attributes) + return TEXTAREA(**attr) class BooleanWidget(FormWidget): _class = 'boolean' @@ -235,7 +253,6 @@ class OptionsWidget(FormWidget): raise SyntaxError( 'widget cannot determine options of %s' % field) opts = [OPTION(v, _value=k) for (k, v) in options] - return SELECT(*opts, **attr) @@ -843,6 +860,7 @@ class SQLFORM(FORM): widgets = Storage(dict( string=StringWidget, text=TextWidget, + json=JSONWidget, password=PasswordWidget, integer=IntegerWidget, double=DoubleWidget, diff --git a/gluon/validators.py b/gluon/validators.py index 6ec44656..276ed395 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -21,6 +21,16 @@ import unicodedata from cStringIO import StringIO from utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE +JSONErrors = (NameError, TypeError, ValueError, AttributeError, + KeyError) +try: + import json as simplejson +except ImportError: + from gluon.contrib import simplejson + from gluon.contrib.simplejson.decoder import JSONDecodeError + JSONErrors += (JSONDecodeError,) + + __all__ = [ 'CLEANUP', 'CRYPT', @@ -53,6 +63,7 @@ __all__ = [ 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', + 'IS_JSON', ] try: @@ -300,6 +311,30 @@ class IS_LENGTH(Validator): return (value, translate(self.error_message) % dict(min=self.minsize, max=self.maxsize)) +class IS_JSON(Validator): + """ + example:: + INPUT(_type='text', _name='name', + requires=IS_JSON(error_message="This is not a valid json input") + + >>> IS_JSON()('{"a": 100}') + ('{"a": 100}', None) + + >>> IS_JSON()('spam1234') + ('spam1234', 'invalid json') + """ + + def __init__(self, error_message='invalid json'): + self.error_message = error_message + + def __call__(self, value): + try: + simplejson.loads(value) + return (value, None) + except JSONErrors: + pass + return (value, translate(self.error_message)) + class IS_IN_SET(Validator): """ From 1b081525948a3e8c81dabd7ec7ec2addde5e277c Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 8 Jan 2013 09:31:18 -0600 Subject: [PATCH 22/75] fixed issue 1229, _class and bootstrap in SQLFORM, thanks Paolo Valleri --- VERSION | 2 +- gluon/sqlhtml.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 23f15a0d..17ca8a9f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.09.22.34 +Version 2.4.1-alpha.2+timestamp.2013.01.08.09.30.18 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 36082b34..7f710e51 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -733,7 +733,7 @@ def formstyle_table2cols(form, fields): def formstyle_divs(form, fields): ''' divs only ''' - table = TAG['']() + table = FIELDSET() for id, label, controls, help in fields: _help = DIV(help, _class='w2p_fc') _controls = DIV(controls, _class='w2p_fw') @@ -765,7 +765,7 @@ def formstyle_ul(form, fields): def formstyle_bootstrap(form, fields): ''' bootstrap format form layout ''' - form['_class'] = 'form-horizontal' + form['_class'] += ' form-horizontal' parent = FIELDSET() for id, label, controls, help in fields: # wrappers @@ -799,12 +799,12 @@ def formstyle_bootstrap(form, fields): if _submit: # submit button has unwrapped label and controls, different class - parent.append(DIV(label, controls, _class='form-actions')) + parent.append(DIV(label, controls, _class='form-actions', _id=id)) # unflag submit (possible side effect) _submit = False else: # unwrapped label - parent.append(DIV(label, _controls, _class='control-group')) + parent.append(DIV(label, _controls, _class='control-group', _id=id)) return parent From 3b56ed3cfd82aa975bf03e436bf430bdda7bdf48 Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 8 Jan 2013 11:45:04 -0600 Subject: [PATCH 23/75] fixed bootstrap class in SQLHTML, de ja vu --- VERSION | 2 +- gluon/reserved_sql_keywords.py | 4 ++++ gluon/sqlhtml.py | 8 ++++---- gluon/tools.py | 12 ++++++------ 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 23f15a0d..6825be0a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.09.22.34 +Version 2.4.1-alpha.2+timestamp.2013.01.08.11.35.00 diff --git a/gluon/reserved_sql_keywords.py b/gluon/reserved_sql_keywords.py index e5b0fd14..320711e6 100644 --- a/gluon/reserved_sql_keywords.py +++ b/gluon/reserved_sql_keywords.py @@ -1686,6 +1686,9 @@ SQLITE = set(( 'WHERE', )) + +MONGODB_NONRESERVED = set(('ID_', 'SAFE',)) + # remove from here when you add a list. JDBCSQLITE = SQLITE DB2 = INFORMIX = INGRES = JDBCPOSTGRESQL = COMMON @@ -1708,6 +1711,7 @@ ADAPTERS = { 'jdbc:sqlite': JDBCSQLITE, 'jdbc:postgres': JDBCPOSTGRESQL, 'common': COMMON, + 'mongodb_nonreserved': MONGODB_NONRESERVED } ADAPTERS['all'] = reduce(lambda a, b: a.union(b), ( diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 36082b34..7f710e51 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -733,7 +733,7 @@ def formstyle_table2cols(form, fields): def formstyle_divs(form, fields): ''' divs only ''' - table = TAG['']() + table = FIELDSET() for id, label, controls, help in fields: _help = DIV(help, _class='w2p_fc') _controls = DIV(controls, _class='w2p_fw') @@ -765,7 +765,7 @@ def formstyle_ul(form, fields): def formstyle_bootstrap(form, fields): ''' bootstrap format form layout ''' - form['_class'] = 'form-horizontal' + form['_class'] += ' form-horizontal' parent = FIELDSET() for id, label, controls, help in fields: # wrappers @@ -799,12 +799,12 @@ def formstyle_bootstrap(form, fields): if _submit: # submit button has unwrapped label and controls, different class - parent.append(DIV(label, controls, _class='form-actions')) + parent.append(DIV(label, controls, _class='form-actions', _id=id)) # unflag submit (possible side effect) _submit = False else: # unwrapped label - parent.append(DIV(label, _controls, _class='control-group')) + parent.append(DIV(label, _controls, _class='control-group', _id=id)) return parent diff --git a/gluon/tools.py b/gluon/tools.py index 4644cc61..a30d45ce 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -3309,14 +3309,14 @@ class Auth(object): restrict_search=False, resolve=True, extra=None, - menugroups=None): + menu_groups=None): if not hasattr(self, '_wiki'): self._wiki = Wiki(self, render=render, manage_permissions=manage_permissions, force_prefix=force_prefix, restrict_search=restrict_search, env=env, extra=extra or {}, - menugroups=menugroups) + menu_groups=menu_groups) else: self._wiki.env.update(env or {}) # if resolve is set to True, process request as wiki call @@ -4726,7 +4726,7 @@ class Wiki(object): def __init__(self, auth, env=None, render='markmin', manage_permissions=False, force_prefix='', - restrict_search=False, extra=None, menugroups=None): + restrict_search=False, extra=None, menu_groups=None): self.env = env or {} self.env['component'] = Wiki.component if render == 'markmin': @@ -4735,7 +4735,7 @@ class Wiki(object): render = self.html_render self.render = render self.auth = auth - self.menugroups = menugroups + self.menu_groups = menu_groups if self.auth.user: self.force_prefix = force_prefix % self.auth.user else: @@ -4853,11 +4853,11 @@ class Wiki(object): return True def can_see_menu(self): - if self.menugroups is None: + if self.menu_groups is None: return True if self.auth.user: groups = self.auth.user_groups.values() - if any(t in self.menugroups for t in groups): + if any(t in self.menu_groups for t in groups): return True return False From 2d606e68365c376b4584f64000d0e6dabcee56d7 Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 8 Jan 2013 14:38:05 -0600 Subject: [PATCH 24/75] actions='#', fixed issue 1253, thanks yaribaud --- VERSION | 2 +- gluon/html.py | 16 ++++++++-------- gluon/reserved_sql_keywords.py | 2 +- gluon/tests/test_html.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 6825be0a..3ab446d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.11.35.00 +Version 2.4.1-alpha.2+timestamp.2013.01.08.14.36.21 diff --git a/gluon/html.py b/gluon/html.py index 5b532b17..265f5500 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -991,7 +991,7 @@ class DIV(XmlComponent): >>> a=FORM( INPUT(_type='text'), SELECT(range(1)), TEXTAREA() ) >>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled' >>> a.xml() - '
' + '
' Elements that are matched can also be replaced or removed by specifying a "replace" argument (note, a list of the original matching elements @@ -1932,7 +1932,7 @@ class FORM(DIV): >>> from validators import IS_NOT_EMPTY >>> form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY())) >>> form.xml() - '
' + '
' a FORM is container for INPUT, TEXTAREA, SELECT and other helpers @@ -2033,7 +2033,7 @@ class FORM(DIV): def _postprocessing(self): if not '_action' in self.attributes: - self['_action'] = '' + self['_action'] = '#' if not '_method' in self.attributes: self['_method'] = 'post' if not '_enctype' in self.attributes: @@ -2415,25 +2415,25 @@ def test():
abc
def
123
>>> form=FORM(INPUT(_type='text', _name='myvar', requires=IS_EXPR('int(value)<10'))) >>> print form.xml() -
+
>>> print form.accepts({'myvar':'34'}, formname=None) False >>> print form.xml() -
invalid expression
+
invalid expression
>>> print form.accepts({'myvar':'4'}, formname=None, keepvalues=True) True >>> print form.xml() -
+
>>> form=FORM(SELECT('cat', 'dog', _name='myvar')) >>> print form.accepts({'myvar':'dog'}, formname=None, keepvalues=True) True >>> print form.xml() -
+
>>> form=FORM(INPUT(_type='text', _name='myvar', requires=IS_MATCH('^\w+$', 'only alphanumeric!'))) >>> print form.accepts({'myvar':'as df'}, formname=None) False >>> print form.xml() -
only alphanumeric!
+
only alphanumeric!
>>> session={} >>> form=FORM(INPUT(value=\"Hello World\", _name=\"var\", requires=IS_MATCH('^\w+$'))) >>> if form.accepts({}, session,formname=None): print 'passed' diff --git a/gluon/reserved_sql_keywords.py b/gluon/reserved_sql_keywords.py index 320711e6..9ff314ae 100644 --- a/gluon/reserved_sql_keywords.py +++ b/gluon/reserved_sql_keywords.py @@ -1687,7 +1687,7 @@ SQLITE = set(( )) -MONGODB_NONRESERVED = set(('ID_', 'SAFE',)) +MONGODB_NONRESERVED = set(('SAFE',)) # remove from here when you add a list. JDBCSQLITE = SQLITE diff --git a/gluon/tests/test_html.py b/gluon/tests/test_html.py index 336eff9b..1273f2bd 100644 --- a/gluon/tests/test_html.py +++ b/gluon/tests/test_html.py @@ -74,7 +74,7 @@ class TestBareHelpers(unittest.TestCase): def testFORM(self): self.assertEqual(FORM('<>', _a='1', _b='2').xml(), - '
<>
') + '
<>
') def testH1(self): self.assertEqual(H1('<>', _a='1', _b='2').xml(), From 4946ebf1547019da20452eca18b5f136adb932c2 Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 8 Jan 2013 15:23:44 -0600 Subject: [PATCH 25/75] fixed issue 1260, auth.settings.update_fields, thanks Nafiux --- VERSION | 2 +- gluon/tools.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 3ab446d1..f74e79d9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.14.36.21 +Version 2.4.1-alpha.2+timestamp.2013.01.08.15.22.56 diff --git a/gluon/tools.py b/gluon/tools.py index a30d45ce..8febc183 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -881,6 +881,7 @@ class Auth(object): profile_fields=None, email_case_sensitive=True, username_case_sensitive=True, + update_fields = ['email'], ) # ## these are messages that can be customized default_messages = dict( @@ -2047,7 +2048,8 @@ class Auth(object): if not self in self.settings.login_methods: # do not store password in db form.vars[passfield] = None - user = self.get_or_create_user(form.vars) + user = self.get_or_create_user( + form.vars, self.settings.update_fields) break if not user: # alternates have failed, maybe because service inaccessible @@ -2067,7 +2069,8 @@ class Auth(object): if not self in self.settings.login_methods: # do not store password in db form.vars[passfield] = None - user = self.get_or_create_user(form.vars) + user = self.get_or_create_user( + form.vars, self.settings.update_fields) break if not user: self.log_event(self.messages.login_failed_log, @@ -2087,7 +2090,8 @@ class Auth(object): if cas_user: cas_user[passfield] = None user = self.get_or_create_user( - table_user._filter_fields(cas_user)) + table_user._filter_fields(cas_user), + self.settings.update_fields) elif hasattr(cas, 'login_form'): return cas.login_form() else: From 980aedf314b4cfb27e6e6a12dcc32fa3e1e45d1d Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 8 Jan 2013 15:31:17 -0600 Subject: [PATCH 26/75] some cleanup patches, thanks Alan --- VERSION | 2 +- gluon/__init__.py | 2 +- gluon/dal.py | 15 ++++----------- gluon/tests/test_dal.py | 9 +++++++-- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/VERSION b/VERSION index f74e79d9..86b688ed 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.15.22.56 +Version 2.4.1-alpha.2+timestamp.2013.01.08.15.30.47 diff --git a/gluon/__init__.py b/gluon/__init__.py index 3e9801f7..da8d5587 100644 --- a/gluon/__init__.py +++ b/gluon/__init__.py @@ -10,7 +10,7 @@ Web2Py framework modules ======================== """ -__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64'] +__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64'] from globals import current from html import * diff --git a/gluon/dal.py b/gluon/dal.py index fbc4c854..126dcd02 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -4253,7 +4253,7 @@ class NoSQLAdapter(BaseAdapter): import json as simplejson except ImportError: import gluon.contrib.simplejson as simplejson - obj = serializers.loads_json(obj) + obj = simplejson.loads(obj) elif is_string and field_is_type('list:string'): return map(self.to_unicode,obj) elif is_list: @@ -5953,13 +5953,8 @@ class IMAPAdapter(NoSQLAdapter): return str(query) def select(self,query,fields,attributes): - """ Search and Fetch records and return web2py - rows + """ Search and Fetch records and return web2py rows """ - # tablename, imapqry_array , fieldnames = self._select(query,fields,attributes) - ######################################## - ############# Start new .select() ###### - # move this statement elsewhere (upper-level) if use_common_filters(query): query = self.common_filter(query, [self.get_query_mailbox(query),]) @@ -6152,10 +6147,6 @@ class IMAPAdapter(NoSQLAdapter): imapqry_array_item.append(item_dict[fieldname]) imapqry_array.append(imapqry_array_item) - # return tablename, imapqry_array, fieldnames - ############# End new .select() ######## - ######################################## - # parse result and return a rows object colnames = fieldnames processor = attributes.get('processor',self.parse) @@ -9614,6 +9605,7 @@ def test_all(): Field('blobf', 'blob'),\ Field('integerf', 'integer', unique=True),\ Field('doublef', 'double', unique=True,notnull=True),\ + Field('jsonf', 'json'),\ Field('datef', 'date', default=datetime.date.today()),\ Field('timef', 'time'),\ Field('datetimef', 'datetime'),\ @@ -9623,6 +9615,7 @@ def test_all(): >>> db.users.insert(stringf='a', booleanf=True, passwordf='p', blobf='0A',\ uploadf=None, integerf=5, doublef=3.14,\ + jsonf={"j": True},\ datef=datetime.date(2001, 1, 1),\ timef=datetime.time(12, 30, 15),\ datetimef=datetime.datetime(2002, 2, 2, 12, 30, 15)) diff --git a/gluon/tests/test_dal.py b/gluon/tests/test_dal.py index d92ac0e6..a703801d 100644 --- a/gluon/tests/test_dal.py +++ b/gluon/tests/test_dal.py @@ -31,6 +31,7 @@ ALLOWED_DATATYPES = [ 'datetime', 'upload', 'password', + 'json', ] @@ -62,14 +63,14 @@ class TestFields(unittest.TestCase): def testFieldTypes(self): - # Check that string, text, and password default length is 512 + # Check that string, and password default length is 512 for typ in ['string', 'password']: self.assert_(Field('abc', typ).length == 512, "Default length for type '%s' is not 512 or 255" % typ) # Check that upload default length is 512 self.assert_(Field('abc', 'upload').length == 512, - "Default length for type 'upload' is not 128") + "Default length for type 'upload' is not 512") # Check that Tables passed in the type creates a reference self.assert_(Field('abc', Table(None, 'temp')).type @@ -113,6 +114,10 @@ class TestFields(unittest.TestCase): self.assertEqual(db.t.insert(a=True), 1) self.assertEqual(db().select(db.t.a)[0].a, True) db.t.drop() + db.define_table('t', Field('a', 'json', default={})) + self.assertEqual(db.t.insert(a={}), 1) + self.assertEqual(db().select(db.t.a)[0].a, {}) + db.t.drop() db.define_table('t', Field('a', 'date', default=datetime.date.today())) t0 = datetime.date.today() From 65bc1062f5b4932a1397d4dfa92bb7e467ec1927 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 09:16:50 -0600 Subject: [PATCH 27/75] fixed add class --- VERSION | 2 +- gluon/sqlhtml.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 86b688ed..152b91b8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.08.15.30.47 +Version 2.4.1-alpha.2+timestamp.2013.01.09.09.16.07 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 7f710e51..db928d58 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -765,7 +765,7 @@ def formstyle_ul(form, fields): def formstyle_bootstrap(form, fields): ''' bootstrap format form layout ''' - form['_class'] += ' form-horizontal' + form.add_class('form-horizontal') parent = FIELDSET() for id, label, controls, help in fields: # wrappers From 4ce3ebf1fddbc8d1d974507cada773369d4f0c5d Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 10:18:42 -0600 Subject: [PATCH 28/75] fixed issue 1262, table.insert() empty, thanks Alan --- VERSION | 2 +- gluon/dal.py | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 152b91b8..59224640 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.09.16.07 +Version 2.4.1-alpha.2+timestamp.2013.01.09.10.17.54 diff --git a/gluon/dal.py b/gluon/dal.py index 126dcd02..0196ba2b 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -8044,13 +8044,24 @@ class Table(object): raise RuntimeError("Unable to handle upload") fields[field.name] = new_name + def _defaults(self, fields): + "If there are no fields/values specified, return table defaults" + if not fields: + fields = {} + for field in self: + if field.type != "id": + fields[field.name] = field.default + return fields + def _insert(self, **fields): - return self._db._adapter._insert(self,self._listify(fields)) + fields = self._default(fields) + return self._db._adapter._insert(self, self._listify(fields)) def insert(self, **fields): + fields = self._defaults(fields) self._attempt_upload(fields) if any(f(fields) for f in self._before_insert): return 0 - ret = self._db._adapter.insert(self,self._listify(fields)) + ret = self._db._adapter.insert(self, self._listify(fields)) if ret and self._after_insert: fields = Row(fields) [f(fields,ret) for f in self._after_insert] From 45e8e20eaed3d19a083bc49ab0954ab52f62e6e5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 10:44:58 -0600 Subject: [PATCH 29/75] parse_as_rest(patterns=[('...',query)] --- VERSION | 2 +- gluon/dal.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 59224640..61c6729f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.10.17.54 +Version 2.4.1-alpha.2+timestamp.2013.01.09.10.44.15 diff --git a/gluon/dal.py b/gluon/dal.py index 0196ba2b..2e7a1569 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7175,9 +7175,15 @@ def index(): return Row({'status':200,'pattern':'list', 'error':None,'response':patterns}) for pattern in patterns: + if isinstance(pattern,tuple): + pattern, basequery = pattern + else: + basequery = None otable=table=None if not isinstance(queries,dict): dbset=db(queries) + if basequery is not None: + dbset = dbset(basequery) i=0 tags = pattern[1:].split('/') if len(tags)!=len(args): @@ -7224,6 +7230,8 @@ def index(): raise RuntimeError("invalid pattern: %s" % pattern) if not otable and isinstance(queries,dict): dbset = db(queries[table]) + if basequery is not None: + dbset = dbset(basequery) dbset=dbset(query) else: raise RuntimeError("missing relation in pattern: %s" % pattern) From f84ac09e31eba8b468a479fc5159be9187eb3703 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 11:13:05 -0600 Subject: [PATCH 30/75] form.custom.delete --- VERSION | 2 +- gluon/sqlhtml.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 61c6729f..fede24d4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.10.44.15 +Version 2.4.1-alpha.2+timestamp.2013.01.09.11.12.16 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index db928d58..261eb525 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1150,7 +1150,7 @@ class SQLFORM(FORM): # # when deletable, add delete? checkbox - self.custom.deletable = '' + self.custom.delete = self.custom.deletable = '' if record and deletable: #add secondary css class for cascade delete warning css = 'delete' @@ -1172,7 +1172,8 @@ class SQLFORM(FORM): _id=self.FIELDKEY_DELETE_RECORD + SQLFORM.ID_LABEL_SUFFIX), widget, col3.get(self.FIELDKEY_DELETE_RECORD, ''))) - self.custom.deletable = widget + self.custom.delete = self.custom.deletable = widget + # when writable, add submit button self.custom.submit = '' From db1a78808224ef8852be76aa319117422331f1f6 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 12:44:35 -0600 Subject: [PATCH 31/75] no warp buttons --- VERSION | 2 +- gluon/sqlhtml.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index fede24d4..f5302b63 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.11.12.16 +Version 2.4.1-alpha.2+timestamp.2013.01.09.12.43.44 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 261eb525..3134e8e0 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1866,7 +1866,7 @@ class SQLFORM(FORM): def buttons(edit=False, view=False, record=None): buttons = DIV(gridbutton('buttonback', 'Back', referrer), - _class='form_header row_buttons %(header)s %(cornertop)s' % ui) + _class='form_header row_buttons %(header)s %(cornertop)s' % ui,_nowrap=True) if edit and (not callable(edit) or edit(record)): args = ['edit', table._tablename, request.args[-1]] buttons.append(gridbutton('buttonedit', 'Edit', From 9e9fde2e8fe1539b2824b8a3a8e05063b6abf0b8 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 12:50:05 -0600 Subject: [PATCH 32/75] no warp buttons --- VERSION | 2 +- gluon/sqlhtml.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index f5302b63..3c19b894 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.12.43.44 +Version 2.4.1-alpha.2+timestamp.2013.01.09.12.49.10 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 3134e8e0..a9cca4d1 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1866,7 +1866,7 @@ class SQLFORM(FORM): def buttons(edit=False, view=False, record=None): buttons = DIV(gridbutton('buttonback', 'Back', referrer), - _class='form_header row_buttons %(header)s %(cornertop)s' % ui,_nowrap=True) + _class='form_header row_buttons %(header)s %(cornertop)s' % ui) if edit and (not callable(edit) or edit(record)): args = ['edit', table._tablename, request.args[-1]] buttons.append(gridbutton('buttonedit', 'Edit', @@ -2279,7 +2279,7 @@ class SQLFORM(FORM): elif not isinstance(value, DIV): value = field.formatter(value) trcols.append(TD(value)) - row_buttons = TD(_class='row_buttons') + row_buttons = TD(_class='row_buttons',_nowrap=True) if links and links_in_grid: toadd = [] for link in links: From f02676f264fb076f20522b4819dfc3644b44c776 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 15:41:21 -0600 Subject: [PATCH 33/75] Wiki.render_base(body) --- VERSION | 2 +- gluon/sqlhtml.py | 31 ++++++++++++++++++------------- gluon/tools.py | 19 ++++++++++++------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/VERSION b/VERSION index 3c19b894..91e8d742 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.12.49.10 +Version 2.4.1-alpha.2+timestamp.2013.01.09.15.40.30 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index a9cca4d1..973185c4 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -540,20 +540,25 @@ class UploadWidget(FormWidget): requires = attr["requires"] if requires == [] or isinstance(requires, IS_EMPTY_OR): - inp = DIV(inp, '[', - A(current.T( - UploadWidget.GENERIC_DESCRIPTION), _href=url), - '|', - INPUT(_type='checkbox', - _name=field.name + cls.ID_DELETE_SUFFIX, - _id=field.name + cls.ID_DELETE_SUFFIX), - LABEL(current.T(cls.DELETE_FILE), - _for=field.name + cls.ID_DELETE_SUFFIX), - ']', br, image) + inp = DIV(inp, + SPAN('[', + A(current.T( + UploadWidget.GENERIC_DESCRIPTION), _href=url), + '|', + INPUT(_type='checkbox', + _name=field.name + cls.ID_DELETE_SUFFIX, + _id=field.name + cls.ID_DELETE_SUFFIX), + LABEL(current.T(cls.DELETE_FILE), + _for=field.name + cls.ID_DELETE_SUFFIX, + _style='display:inline'), + ']', _style='white-space:nowrap'), + br, image) else: - inp = DIV(inp, '[', - A(cls.GENERIC_DESCRIPTION, _href=url), - ']', br, image) + inp = DIV(inp, + SPAN('[', + A(cls.GENERIC_DESCRIPTION, _href=url), + ']', _style='white-space:nowrap'), + br, image) return inp @classmethod diff --git a/gluon/tools.py b/gluon/tools.py index 8febc183..de3aadaf 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4698,15 +4698,19 @@ class Expose(object): class Wiki(object): everybody = 'everybody' rows_page = 25 - - def markmin_render(self, page): - html = MARKMIN(page.body, extra=self.extra, + def markmin_base(self,body): + return MARKMIN(body, extra=self.extra, url=True, environment=self.env, autolinks=lambda link: expand_one(link, {})).xml() - html += DIV(_class='w2p_wiki_tags', - *[A(t.strip(), _href=URL(args='_search', vars=dict(q=t))) - for t in page.tags or [] if t.strip()]).xml() - return html + + def render_tags(self, tags): + return DIV( + _class='w2p_wiki_tags', + *[A(t.strip(), _href=URL(args='_search', vars=dict(q=t))) + for t in page.tags or [] if t.strip()]) + + def markmin_render(self, page): + return self.markmin_base(page.body) + self.render_tags(page.tags).xml() def html_render(self, page): html = page.body @@ -4716,6 +4720,7 @@ class Wiki(object): html = replace_autolinks(html, lambda link: expand_one(link, {})) # @{component:name} -> html = replace_components(html, self.env) + html = html + self.render_tags(page.tags).xml() return html @staticmethod From 6ebed79500b1161ff1f4e225cb11661d58db1747 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 16:10:33 -0600 Subject: [PATCH 34/75] fixed problem with render_tags --- VERSION | 2 +- gluon/tools.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 91e8d742..d36c6543 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.15.40.30 +Version 2.4.1-alpha.2+timestamp.2013.01.09.16.09.41 diff --git a/gluon/tools.py b/gluon/tools.py index de3aadaf..4c5d6d3c 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4707,7 +4707,7 @@ class Wiki(object): return DIV( _class='w2p_wiki_tags', *[A(t.strip(), _href=URL(args='_search', vars=dict(q=t))) - for t in page.tags or [] if t.strip()]) + for t in tags or [] if t.strip()]) def markmin_render(self, page): return self.markmin_base(page.body) + self.render_tags(page.tags).xml() From 90dbf440c7ff45c617eabcd74baadf52c71cace5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 9 Jan 2013 16:57:19 -0600 Subject: [PATCH 35/75] optional mail.settings.sender, thanks Michele --- VERSION | 2 +- gluon/tools.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index d36c6543..73372cfc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.16.09.41 +Version 2.4.1-alpha.2+timestamp.2013.01.09.16.56.34 diff --git a/gluon/tools.py b/gluon/tools.py index 4c5d6d3c..0244fee9 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -272,7 +272,7 @@ class Mail(object): cc=None, bcc=None, reply_to=None, - sender='%(sender)s', + sender=None, encoding='utf-8', raw=False, headers={} @@ -360,9 +360,11 @@ class Mail(object): text = encode_header(text) return text + sender = sender or self.settings.sender + if not isinstance(self.settings.server, str): raise Exception('Server address not specified') - if not isinstance(self.settings.sender, str): + if not isinstance(sender, str): raise Exception('Sender address not specified') if not raw: @@ -458,11 +460,11 @@ class Mail(object): c.set_armor(1) c.signers_clear() # search for signing key for From: - for sigkey in c.op_keylist_all(self.settings.sender, 1): + for sigkey in c.op_keylist_all(sender, 1): if sigkey.can_sign: c.signers_add(sigkey) if not c.signers_enum(0): - self.error = 'No key for signing [%s]' % self.settings.sender + self.error = 'No key for signing [%s]' % sender return False c.set_passphrase_cb(lambda x, y, z: sign_passphrase) try: @@ -619,7 +621,6 @@ class Mail(object): # no cryptography process as usual payload = payload_in - sender = sender % dict(sender=self.settings.sender) payload['From'] = encoded_or_raw(sender.decode(encoding)) origTo = to[:] if to: @@ -655,16 +656,16 @@ class Mail(object): attachments = attachments and [(a.my_filename, a.my_payload) for a in attachments if not raw] if attachments: result = mail.send_mail( - sender=self.settings.sender, to=origTo, + sender=sender, to=origTo, subject=subject, body=text, html=html, attachments=attachments, **xcc) elif html and (not raw): result = mail.send_mail( - sender=self.settings.sender, to=origTo, + sender=sender, to=origTo, subject=subject, body=text, html=html, **xcc) else: result = mail.send_mail( - sender=self.settings.sender, to=origTo, + sender=sender, to=origTo, subject=subject, body=text, **xcc) else: smtp_args = self.settings.server.split(':') @@ -679,7 +680,7 @@ class Mail(object): if self.settings.login: server.login(*self.settings.login.split(':', 1)) result = server.sendmail( - self.settings.sender, to, payload.as_string()) + sender, to, payload.as_string()) server.quit() except Exception, e: logger.warn('Mail.send failure:%s' % e) From 8eadfe3d790ff2473dfedd912d654304a179aea3 Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 10 Jan 2013 13:01:12 -0600 Subject: [PATCH 36/75] fixed issue 1268, better grid search options, thanks Paolo Angulo --- VERSION | 2 +- gluon/globals.py | 6 ++++-- gluon/sqlhtml.py | 32 +++++++++++++++++++++++++------- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/VERSION b/VERSION index 73372cfc..c2bfd314 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.09.16.56.34 +Version 2.4.1-alpha.2+timestamp.2013.01.10.12.59.43 diff --git a/gluon/globals.py b/gluon/globals.py index c67348c1..11f3c7e5 100644 --- a/gluon/globals.py +++ b/gluon/globals.py @@ -375,7 +375,7 @@ class Response(Storage): wrapped = streamer(stream, chunk_size=chunk_size) return wrapped - def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True): + def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True, download_filename=None): """ example of usage in controller:: @@ -403,9 +403,11 @@ class Response(Storage): raise HTTP(404) headers = self.headers headers['Content-Type'] = contenttype(name) + if download_filename == None: + download_filename = filename if attachment: headers['Content-Disposition'] = \ - 'attachment; filename="%s"' % filename.replace('"','\"') + 'attachment; filename="%s"' % download_filename.replace('"','\"') return self.stream(stream, chunk_size=chunk_size, request=request) def json(self, data, default=None): diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 973185c4..2f73fd6f 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1623,7 +1623,7 @@ class SQLFORM(FORM): 'integer': ['=', '!=', '<', '>', '<=', '>=', 'in', 'not in'], 'double': ['=', '!=', '<', '>', '<=', '>='], 'id': ['=', '!=', '<', '>', '<=', '>=', 'in', 'not in'], - 'reference': ['=', '!=', '<', '>', '<=', '>=', 'in', 'not in'], + 'reference': ['=', '!='], 'boolean': ['=', '!=']} if fields[0]._db._adapter.dbengine == 'google:datastore': search_options['string'] = ['=', '!=', '<', '>', '<=', '>='] @@ -1641,15 +1641,33 @@ class SQLFORM(FORM): field.label, str) and T(field.label) or field.label selectfields.append(OPTION(label, _value=str(field))) operators = SELECT(*[OPTION(T(option), _value=option) for option in options]) + _id = "%s_%s" % (value_id,name) if field.type == 'boolean': + value_input = SQLFORM.widgets.boolean.widget(field,field.default,_id=_id) + elif field.type == 'double': + value_input = SQLFORM.widgets.double.widget(field,field.default,_id=_id) + elif field.type == 'time': + value_input = SQLFORM.widgets.time.widget(field,field.default,_id=_id) + elif field.type == 'date': + value_input = SQLFORM.widgets.date.widget(field,field.default,_id=_id) + elif field.type == 'datetime': + value_input = SQLFORM.widgets.datetime.widget(field,field.default,_id=_id) + elif (field.type.startswith('reference ') or + field.type.startswith('list:reference ')) and \ + hasattr(field.requires,'options'): value_input = SELECT( - OPTION(T("True"), _value="T"), - OPTION(T("False"), _value="F"), - _id="%s_%s" % (value_id,name)) + *[OPTION(v, _value=k) + for k,v in field.requires.options()], + **dict(_id=_id)) + elif field.type == 'integer' or \ + field.type.startswith('reference ') or \ + field.type.startswith('list:integer') or \ + field.type.startswith('list:reference '): + value_input = SQLFORM.widgets.integer.widget(field,field.default,_id=_id) else: - value_input = INPUT(_type='text', - _id="%s_%s" % (value_id,name), - _class=field.type) + value_input = INPUT( + _type='text', _id=_id, _class=field.type) + new_button = INPUT( _type="button", _value=T('New'), _class="btn", _onclick="%s_build_query('new','%s')" % (prefix,field)) From 7614955591f1bf75b248ad678e3112e5ebf9233f Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 10 Jan 2013 13:04:38 -0600 Subject: [PATCH 37/75] IMAP DAL enhancements, thanks Alan --- VERSION | 2 +- gluon/dal.py | 49 ++++++++++++++++++++++++++++++++++++------------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/VERSION b/VERSION index c2bfd314..2dd0f451 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.12.59.43 +Version 2.4.1-alpha.2+timestamp.2013.01.10.13.04.10 diff --git a/gluon/dal.py b/gluon/dal.py index 2e7a1569..3c66f9d0 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -5670,6 +5670,7 @@ class IMAPAdapter(NoSQLAdapter): self.driver_args = driver_args self.adapter_args = adapter_args self.mailbox_size = None + self.static_names = None self.charset = sys.getfilesystemencoding() # imap class self.imap4 = None @@ -5848,11 +5849,17 @@ class IMAPAdapter(NoSQLAdapter): return charset def reset_mailboxes(self): - self.connection.mailbox_names = None + "Forces the adapter to retrieve mailbox names from the server" + self.connection.mailbox_names = self.static_names = None self.get_mailboxes() def get_mailboxes(self): """ Query the mail database for mailbox names """ + if self.static_names: + # statically defined mailbox names + self.connection.mailbox_names = self.static_names + return self.static_names.keys() + mailboxes_list = self.connection.list() self.connection.mailbox_names = dict() mailboxes = list() @@ -5864,7 +5871,8 @@ class IMAPAdapter(NoSQLAdapter): sub_items = item.split("\"") sub_items = [sub_item for sub_item in sub_items \ if len(sub_item.strip()) > 0] - mailbox = sub_items[len(sub_items) - 1] + # mailbox = sub_items[len(sub_items) -1] + mailbox = sub_items[-1] # remove unwanted characters and store original names # Don't allow leading non alphabetic characters mailbox_name = re.sub('^[_0-9]*', '', re.sub('[^_\w]','',re.sub('[/ ]','_',mailbox))) @@ -5896,7 +5904,7 @@ class IMAPAdapter(NoSQLAdapter): else: return False - def define_tables(self): + def define_tables(self, mailbox_names=None): """ Auto create common IMAP fileds @@ -5908,11 +5916,16 @@ class IMAPAdapter(NoSQLAdapter): Returns a dictionary with tablename, server native mailbox name pairs. """ + if mailbox_names: + # optional statically declared mailboxes + self.static_names = mailbox_names if not isinstance(self.connection.mailbox_names, dict): self.get_mailboxes() - mailboxes = self.connection.mailbox_names.keys() - for mailbox_name in mailboxes: - self.db.define_table("%s" % mailbox_name, + + names = self.connection.mailbox_names.keys() + + for name in names: + self.db.define_table("%s" % name, Field("uid", "string", writable=False), Field("answered", "boolean"), Field("created", "datetime", writable=False), @@ -5935,8 +5948,8 @@ class IMAPAdapter(NoSQLAdapter): # Set a special _mailbox attribute for storing # native mailbox names - self.db[mailbox_name].mailbox = \ - self.connection.mailbox_names[mailbox_name] + self.db[name].mailbox = \ + self.connection.mailbox_names[name] # Set the db instance mailbox collections self.db.mailboxes = self.connection.mailbox_names @@ -5969,10 +5982,14 @@ class IMAPAdapter(NoSQLAdapter): if isinstance(query, Query): tablename = self.get_table(query) mailbox = self.connection.mailbox_names.get(tablename, None) - if mailbox is not None: + if mailbox is None: + raise ValueError("Mailbox name not found: %s" % mailbox) + else: # select with readonly - selected = self.connection.select(mailbox, True) - self.mailbox_size = int(selected[1][0]) + result, selected = self.connection.select(mailbox, True) + if result != "OK": + raise Exception("IMAP error: %s" % selected) + self.mailbox_size = int(selected[0]) search_query = "(%s)" % str(query).strip() search_result = self.connection.uid("search", None, search_query) # Normal IMAP response OK is assumed (change this) @@ -6014,10 +6031,12 @@ class IMAPAdapter(NoSQLAdapter): fetch_results.append(fr) else: # error retrieving the flags for this message - pass + raise Exception("IMAP error retrieving flags: %s" % fdata) else: # error retrieving the message body - pass + raise Exception("IMAP error retrieving the body: %s" % data) + else: + raise Exception("IMAP search error: %s" % search_result[1]) elif isinstance(query, (Expression, basestring)): raise NotImplementedError() else: @@ -6193,6 +6212,8 @@ class IMAPAdapter(NoSQLAdapter): result, data = self.connection.store(*command) if result == "OK": rowcount += 1 + else: + raise Exception("IMAP storing error: %s" % data) return rowcount def _count(self, query, distinct=None): @@ -6224,6 +6245,8 @@ class IMAPAdapter(NoSQLAdapter): result, data = self.connection.store(number, "+FLAGS", "(\\Deleted)") if result == "OK": counter += 1 + else: + raise Exception("IMAP store error: %s" % data) if counter > 0: result, data = self.connection.expunge() return counter From b20d963f568bbd5dfd9d835448ba69226e4fae34 Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 10 Jan 2013 14:29:50 -0600 Subject: [PATCH 38/75] No more templates in wiki, security vulnearbility --- VERSION | 2 +- gluon/tools.py | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/VERSION b/VERSION index 2dd0f451..0fc639e9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.13.04.10 +Version 2.4.1-alpha.2+timestamp.2013.01.10.14.29.15 diff --git a/gluon/tools.py b/gluon/tools.py index 0244fee9..de570913 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4970,7 +4970,7 @@ class Wiki(object): slug.startswith(self.force_prefix)): current.session.flash = 'slug must have "%s" prefix' \ % self.force_prefix - redirect(URL(args=('_edit', self.force_prefix + slug))) + redirect(URL(args=('_create'))) db.wiki_page.can_read.default = [Wiki.everybody] db.wiki_page.can_edit.default = [auth.user_group_role()] db.wiki_page.title.default = title_guess @@ -4978,8 +4978,8 @@ class Wiki(object): if slug == 'wiki-menu': db.wiki_page.body.default = \ '- Menu Item > @////index\n- - Submenu > http://web2py.com' - else: - db.wiki_page.body.default = db(db.wiki_page.id==from_template).select(db.wiki_page.body)[0].body if int(from_template) > 0 else '## %s\n\npage content' % title_guess + #else: + # db.wiki_page.body.default = db(db.wiki_page.id==from_template).select(db.wiki_page.body)[0].body if int(from_template) > 0 else '## %s\n\npage content' % title_guess vars = current.request.post_vars if vars.body: vars.body = vars.body.replace('://%s' % self.host, '://HOSTNAME') @@ -5071,13 +5071,13 @@ class Wiki(object): slugs=db(db.wiki_page.id>0).select(db.wiki_page.id,db.wiki_page.slug) options=[OPTION(row.slug,_value=row.id) for row in slugs] options.insert(0, OPTION('',_value='')) - form = SQLFORM.factory(Field("slug", default=current.request.args(1), + form = SQLFORM.factory(Field("slug", default=current.request.args(1) or self.force_prefix, requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))), - Field("from_template", "reference wiki_page", - requires=IS_EMPTY_OR(IS_IN_DB(db, db.wiki_page, '%(slug)s')), - comment=current.T("Choose Template or empty for new Page")), - _class="well span6") + #Field("from_template", "reference wiki_page", + # requires=IS_EMPTY_OR(IS_IN_DB(db, db.wiki_page, '%(slug)s')), + # comment=current.T("Choose Template or empty for new Page")), + _class="well span6") form.element("[type=submit]").attributes["_value"] = current.T("Create Page from Slug") if form.process().accepted: From 70d701db5b6bf10ca97f5aa21fb8a1c1122339e4 Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 10 Jan 2013 14:54:21 -0600 Subject: [PATCH 39/75] changed pages grid in auth.wiki --- VERSION | 2 +- gluon/tools.py | 17 +++++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/VERSION b/VERSION index 0fc639e9..b2aa09b2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.14.29.15 +Version 2.4.1-alpha.2+timestamp.2013.01.10.14.53.47 diff --git a/gluon/tools.py b/gluon/tools.py index de570913..2be50b3b 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4761,7 +4761,7 @@ class Wiki(object): Field('slug', requires=[IS_SLUG(), IS_NOT_IN_DB(db, 'wiki_page.slug')], - readable=False, writable=False), + writable=False), Field('title', unique=True), Field('body', 'text', notnull=True), Field('tags', 'list:string'), @@ -5088,21 +5088,26 @@ class Wiki(object): def pages(self): if not self.can_manage(): return self.not_authorized() - self.auth.db.wiki_page.id.represent = lambda id, row: SPAN( - '@////%s' % row.slug) + self.auth.db.wiki_page.slug.represent = lambda slug, row: SPAN( + '@////%s' % slug) self.auth.db.wiki_page.title.represent = lambda title, row: \ A(title, _href=URL(args=row.slug)) + wiki_table = self.auth.db.wiki_page content = SQLFORM.grid( - self.auth.db.wiki_page, + wiki_table, + fields = [wiki_table.slug, + wiki_table.title, wiki_table.tags, + wiki_table.can_read, wiki_table.can_edit], links=[ lambda row: - A('edit', _href=URL(args=('_edit', row.slug))), + A('edit', _href=URL(args=('_edit', row.slug)),_class='btn'), lambda row: - A('media', _href=URL(args=('_editmedia', row.slug)))], + A('media', _href=URL(args=('_editmedia', row.slug)),_class='btn')], details=False, editable=False, deletable=False, create=False, orderby=self.auth.db.wiki_page.title, args=['_pages'], user_signature=False) + return dict(content=content) def media(self, id): From b8cfc9630fd9fc0f71ef24899300208bb9cd4a6e Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 10 Jan 2013 15:13:29 -0600 Subject: [PATCH 40/75] fixed preview of auth.wiki pages --- VERSION | 2 +- gluon/tools.py | 31 +++++++++++++++---------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/VERSION b/VERSION index b2aa09b2..9041eeab 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.14.53.47 +Version 2.4.1-alpha.2+timestamp.2013.01.10.15.12.56 diff --git a/gluon/tools.py b/gluon/tools.py index 2be50b3b..b2f146f3 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4993,20 +4993,19 @@ class Wiki(object): redirect(URL(args=slug)) script = """ $(function() { - if (!$('#wiki_page_body').length) return; - var pagecontent = $('#wiki_page_body'); + if (!jQuery('#wiki_page_body').length) return; + var pagecontent = jQuery('#wiki_page_body'); pagecontent.css('font-family', 'Monaco,Menlo,Consolas,"Courier New",monospace'); - var prevbutton = $(''); - var mediabutton = $(''); - var preview = $('
').hide(); - var previewmedia = $('
'); - var table = $('form'); - var bodylabel = $('#wiki_page_body__label'); - preview.insertBefore(pagecontent); - prevbutton.insertAfter(bodylabel); - mediabutton.insertBefore(table); - previewmedia.insertBefore(table); + var prevbutton = jQuery(''); + var mediabutton = jQuery(''); + var preview = jQuery('
').hide(); + var previewmedia = jQuery('
'); + var form = pagecontent.closest('form'); + preview.insertBefore(form); + prevbutton.insertBefore(form); + mediabutton.insertBefore(form); + previewmedia.insertBefore(form); mediabutton.toggle(function() { web2py_component('%(urlmedia)s', 'previewmedia'); }, function() { @@ -5017,12 +5016,12 @@ class Wiki(object): if (prevbutton.hasClass('nopreview')) { prevbutton.addClass('preview').removeClass( 'nopreview').html('Edit Source'); - web2py_ajax_page('post', '%(url)s', {body : $('#wiki_page_body').val()}, 'preview'); - pagecontent.fadeOut('fast', function() {preview.fadeIn()}); + web2py_ajax_page('post', '%(url)s', {body : jQuery('#wiki_page_body').val()}, 'preview'); + form.fadeOut('fast', function() {preview.fadeIn()}); } else { prevbutton.addClass( 'nopreview').removeClass('preview').html('Preview'); - preview.fadeOut('fast', function() {pagecontent.fadeIn()}); + preview.fadeOut('fast', function() {form.fadeIn()}); } }) }) @@ -5046,7 +5045,7 @@ class Wiki(object): csv = True create = True if current.request.vars.embedded: - script = "var c = $('#wiki_page_body'); c.val(c.val() + $('%s').text()); return false;" + script = "var c = jQuery('#wiki_page_body'); c.val(c.val() + jQuery('%s').text()); return false;" fragment = self.auth.db.wiki_media.id.represent csv = False create = False From 7c5c27f556fdb765d6254ba4af1cb7a4ae8a447b Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 10 Jan 2013 23:12:01 -0600 Subject: [PATCH 41/75] links to user have _rel='nofollow' --- VERSION | 2 +- gluon/tools.py | 35 +++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/VERSION b/VERSION index 9041eeab..142dad40 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.15.12.56 +Version 2.4.1-alpha.2+timestamp.2013.01.10.23.11.16 diff --git a/gluon/tools.py b/gluon/tools.py index b2f146f3..be485211 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -1256,6 +1256,9 @@ class Auth(object): def navbar(self, prefix='Welcome', action=None, separators=(' [ ', ' | ', ' ] '), user_identifier=DEFAULT, referrer_actions=DEFAULT, mode='default'): + def Anr(*a,**b): + b['_rel']='nofollow' + return A(*a,**b) referrer_actions = [] if not referrer_actions else referrer_actions request = current.request asdropdown = (mode == 'dropdown') @@ -1286,19 +1289,19 @@ class Auth(object): user_identifier = user_identifier % self.user if not user_identifier: user_identifier = '' - logout = A(T('Logout'), _href='%s/logout?_next=%s' % + logout = Anr(T('Logout'), _href='%s/logout?_next=%s' % (action, urllib.quote(self.settings.logout_next))) - profile = A(T('Profile'), _href=href('profile')) - password = A(T('Password'), _href=href('change_password')) + profile = Anr(T('Profile'), _href=href('profile')) + password = Anr(T('Password'), _href=href('change_password')) bar = SPAN( prefix, user_identifier, s1, logout, s3, _class='auth_navbar') if asdropdown: - logout = LI(A(I(_class='icon-off'), ' ' + T('Logout'), _href='%s/logout?_next=%s' % + logout = LI(Anr(I(_class='icon-off'), ' ' + T('Logout'), _href='%s/logout?_next=%s' % (action, urllib.quote(self.settings.logout_next)))) # the space before T('Logout') is intentional. It creates a gap between icon and text - profile = LI(A(I(_class='icon-user'), ' ' + + profile = LI(Anr(I(_class='icon-user'), ' ' + T('Profile'), _href=href('profile'))) - password = LI(A(I(_class='icon-lock'), ' ' + + password = LI(Anr(I(_class='icon-lock'), ' ' + T('Password'), _href=href('change_password'))) bar = UL(logout, _class='dropdown-menu') # logout will be the last item in list @@ -1312,21 +1315,21 @@ class Auth(object): bar.insert(-1, s2) bar.insert(-1, password) else: - login = A(T('Login'), _href=href('login')) - register = A(T('Register'), _href=href('register')) - retrieve_username = A( + login = Anr(T('Login'), _href=href('login')) + register = Anr(T('Register'), _href=href('register')) + retrieve_username = Anr( T('Forgot username?'), _href=href('retrieve_username')) - lost_password = A( + lost_password = Anr( T('Lost password?'), _href=href('request_reset_password')) bar = SPAN(s1, login, s3, _class='auth_navbar') if asdropdown: - login = LI(A(I(_class='icon-off'), ' ' + T('Login'), _href=href('login'))) # the space before T('Login') is intentional. It creates a gap between icon and text - register = LI(A(I(_class='icon-user'), + login = LI(Anr(I(_class='icon-off'), ' ' + T('Login'), _href=href('login'))) # the space before T('Login') is intentional. It creates a gap between icon and text + register = LI(Anr(I(_class='icon-user'), ' ' + T('Register'), _href=href('register'))) - retrieve_username = LI(A(I(_class='icon-edit'), ' ' + T( + retrieve_username = LI(Anr(I(_class='icon-edit'), ' ' + T( 'Forgot username?'), _href=href('retrieve_username'))) - lost_password = LI(A(I(_class='icon-lock'), ' ' + T( + lost_password = LI(Anr(I(_class='icon-lock'), ' ' + T( 'Lost password?'), _href=href('request_reset_password'))) bar = UL(login, _class='dropdown-menu') # login will be the last item in list @@ -1349,10 +1352,10 @@ class Auth(object): if asdropdown: bar.insert(-1, LI('', _class='divider')) if self.user_id: - bar = LI(A(prefix, user_identifier, _href='#'), + bar = LI(Anr(prefix, user_identifier, _href='#'), bar, _class='dropdown') else: - bar = LI(A(T('Login'), _href='#'), + bar = LI(Anr(T('Login'), _href='#'), bar, _class='dropdown') return bar From cc66524e1f3ff1304a10c0e5adfd4e5e3e785f33 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Thu, 10 Jan 2013 23:15:46 -0600 Subject: [PATCH 42/75] imapdb(query).select(imapdb.INBOX.sender, imapdb.INBOX.subject, imapdb.INBOX.seen), issue 1269, thanks Alan --- VERSION | 2 +- gluon/dal.py | 52 ++++++++++++++++++++++++++++------------------------ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/VERSION b/VERSION index 142dad40..5f7f56c7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.23.11.16 +Version 2.4.1-alpha.2+timestamp.2013.01.10.23.15.01 diff --git a/gluon/dal.py b/gluon/dal.py index 3c66f9d0..08e6cb7d 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -5623,11 +5623,22 @@ class IMAPAdapter(NoSQLAdapter): # mapped names (which native # mailbox has what table name) - db.mailboxes # tablename, server native name pairs + imapdb.mailboxes # tablename, server native name pairs # To retrieve a table native mailbox name use: - db..mailbox + imapdb.
.mailbox + ### New features v2.4.1: + + # Declare mailboxes statically with tablename, name pairs + # This avoids the extra server names retrieval + + imapdb.define_tables({"inbox": "INBOX"}) + + # Selects without content/attachments/email columns will only + # fetch header and flags + + imapdb(q).select(imapdb.INBOX.sender, imapdb.INBOX.subject) """ types = { @@ -5848,11 +5859,6 @@ class IMAPAdapter(NoSQLAdapter): charset = message.get_content_charset() return charset - def reset_mailboxes(self): - "Forces the adapter to retrieve mailbox names from the server" - self.connection.mailbox_names = self.static_names = None - self.get_mailboxes() - def get_mailboxes(self): """ Query the mail database for mailbox names """ if self.static_names: @@ -5919,6 +5925,8 @@ class IMAPAdapter(NoSQLAdapter): if mailbox_names: # optional statically declared mailboxes self.static_names = mailbox_names + else: + self.static_names = None if not isinstance(self.connection.mailbox_names, dict): self.get_mailboxes() @@ -5965,7 +5973,7 @@ class IMAPAdapter(NoSQLAdapter): query = self.common_filter(query, [self.get_query_mailbox(query),]) return str(query) - def select(self,query,fields,attributes): + def select(self, query, fields, attributes): """ Search and Fetch records and return web2py rows """ # move this statement elsewhere (upper-level) @@ -5998,18 +6006,22 @@ class IMAPAdapter(NoSQLAdapter): # ten records (change for non-experimental implementation) # However, light responses are not guaranteed with this # approach, just fewer messages. - # TODO: change limitby single to 2-tuple argument limitby = attributes.get('limitby', None) messages_set = search_result[1][0].split() # descending order messages_set.reverse() if limitby is not None: - # TODO: asc/desc attributes + # TODO: orderby, asc/desc, limitby from complete message set messages_set = messages_set[int(limitby[0]):int(limitby[1])] - # Partial fetches are not used since the email - # library does not seem to support it (it converts - # partial messages to mangled message instances) - imap_fields = "(RFC822)" + + # keep the requests small for header/flags + if any([(field.name in ["content", + "attachments", "email"]) for + field in fields]): + imap_fields = "(RFC822 FLAGS)" + else: + imap_fields = "(RFC822.HEADER FLAGS)" + if len(messages_set) > 0: # create fetch results object list # fetch each remote message and store it in memmory @@ -6025,13 +6037,8 @@ class IMAPAdapter(NoSQLAdapter): "raw_message": data[0][1]} fr["multipart"] = fr["email"].is_multipart() # fetch flags for the message - ftyp, fdata = self.connection.uid("fetch", uid, "(FLAGS)") - if ftyp == "OK": - fr["flags"] = self.driver.ParseFlags(fdata[0]) - fetch_results.append(fr) - else: - # error retrieving the flags for this message - raise Exception("IMAP error retrieving flags: %s" % fdata) + fr["flags"] = self.driver.ParseFlags(data[1]) + fetch_results.append(fr) else: # error retrieving the message body raise Exception("IMAP error retrieving the body: %s" % data) @@ -6090,9 +6097,6 @@ class IMAPAdapter(NoSQLAdapter): # If there is no encoding found in the message header # force utf-8 replacing characters (change this to # module's defaults). Applies to .sender, .to, .cc and .bcc fields - ############################################################################# - # TODO: External function to manage encoding and decoding of message strings - ############################################################################# item_dict["%s.sender" % tablename] = self.encode_text(message["From"], charset) if "%s.to" % tablename in fieldnames: item_dict["%s.to" % tablename] = self.encode_text(message["To"], charset) From ec8d9a37148b12ee40842bb958ee9c0548616687 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 11 Jan 2013 09:02:22 -0600 Subject: [PATCH 43/75] fixed issue with hiding password, thanks Niphlod --- VERSION | 2 +- gluon/dal.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 5f7f56c7..b1800cab 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.10.23.15.01 +Version 2.4.1-alpha.2+timestamp.2013.01.11.09.01.31 diff --git a/gluon/dal.py b/gluon/dal.py index 08e6cb7d..b7bd6f78 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -447,7 +447,7 @@ def pluralize(singular, rules=PLURALIZE_RULES): if plural: return plural def hide_password(uri): - return REGEX_PASSWORD.sub('://******:',uri) + return REGEX_NOPASSWD.sub('******',uri) def OR(a,b): return a|b @@ -6884,7 +6884,7 @@ class DAL(object): for db in db_group: if not db._uri: continue - k = REGEX_NOPASSWD.sub('******',db._uri) + k = hide_password(db._uri) infos[k] = dict(dbstats = [(row[0], row[1]) for row in db._timings], dbtables = {'defined': sorted(list(set(db.tables) - From bb6e130bfe4973ce5849e92fce1c33926379903a Mon Sep 17 00:00:00 2001 From: mdipierro Date: Fri, 11 Jan 2013 13:38:03 -0600 Subject: [PATCH 44/75] fixed typo in ru.py, issue 1271, thanks Dmitry.Mosin --- VERSION | 2 +- applications/admin/languages/ru.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index b1800cab..1a542db2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.11.09.01.31 +Version 2.4.1-alpha.2+timestamp.2013.01.11.13.37.06 diff --git a/applications/admin/languages/ru.py b/applications/admin/languages/ru.py index 60689f0b..ed4cb893 100644 --- a/applications/admin/languages/ru.py +++ b/applications/admin/languages/ru.py @@ -134,7 +134,7 @@ 'Editing Language file': 'Правка языкового файла', 'Editing Plural Forms File': 'Editing Plural Forms File', 'Enterprise Web Framework': 'Enterprise Web Framework', -'Error logs for "%(app)s"': 'Журнал ошибок для "%(app)"', +'Error logs for "%(app)s"': 'Журнал ошибок для "%(app)s"', 'Error snapshot': 'Error snapshot', 'Error ticket': 'Error ticket', 'Errors': 'Ошибка', From f02735de817d91701505b8e4234b5ac7dafe4515 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 12 Jan 2013 14:04:15 -0600 Subject: [PATCH 45/75] row.as_son, rows.as_json, rows.as_xml, thanks Alan --- VERSION | 2 +- gluon/dal.py | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 1a542db2..29bbc971 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.11.13.37.06 +Version 2.4.1-alpha.2+timestamp.2013.01.12.14.03.31 diff --git a/gluon/dal.py b/gluon/dal.py index b7bd6f78..3b001bb8 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6689,6 +6689,40 @@ class Row(object): del d[k] return d + def as_json(self, mode='object', default=None, **kwargs): + """ + serializes the table to a JSON list of objects + + kwargs are passed to .as_dict method + """ + mode = mode.lower() + if not mode in ['object', 'array']: + raise SyntaxError('Invalid JSON serialization mode: %s' % mode) + + multi = any([isinstance(v, self.__class__) for v in self.values()]) + item = dict() + + if multi: + for k, v in self.as_dict(**kwargs).iteritems(): + item.update(v) + else: + item = self.as_dict(**kwargs) + + if mode != 'object': + item = item.values() + + if have_serializers: + return serializers.json(item, + default=default or + serializers.custom_json) + else: + try: + import json as simplejson + except ImportError: + import gluon.contrib.simplejson as simplejson + return simplejson.dumps(item) + + ################################################################################ # Everything below should be independent of the specifics of the database # and should work for RDBMs and some NoSQL databases @@ -9591,7 +9625,10 @@ class Rows(object): import sqlhtml return sqlhtml.SQLTABLE(self).xml() - def json(self, mode='object', default=None): + def as_xml(self,row_name='row',rows_name='rows'): + return self.xml(strict=True, row_name=row_name, rows_name=rows_name) + + def as_json(self, mode='object', default=None): """ serializes the table to a JSON list of objects """ @@ -9633,6 +9670,7 @@ class Rows(object): import gluon.contrib.simplejson as simplejson return simplejson.dumps(items) + json = as_json ################################################################################ # dummy function used to define some doctests From b747ac27cbfe84f71899a19aca40d05b41ea8b91 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 12 Jan 2013 14:09:05 -0600 Subject: [PATCH 46/75] row.as_json simplification thanks Alan --- VERSION | 2 +- gluon/dal.py | 14 ++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index 29bbc971..916dfd8f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.12.14.03.31 +Version 2.4.1-alpha.2+timestamp.2013.01.12.14.08.26 diff --git a/gluon/dal.py b/gluon/dal.py index 3b001bb8..0d7ecfef 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6689,28 +6689,22 @@ class Row(object): del d[k] return d - def as_json(self, mode='object', default=None, **kwargs): + def as_json(self, default=None, **kwargs): """ serializes the table to a JSON list of objects - kwargs are passed to .as_dict method + only "object" mode supported + TODO: return array mode with query column order """ - mode = mode.lower() - if not mode in ['object', 'array']: - raise SyntaxError('Invalid JSON serialization mode: %s' % mode) - multi = any([isinstance(v, self.__class__) for v in self.values()]) item = dict() if multi: - for k, v in self.as_dict(**kwargs).iteritems(): + for v in self.as_dict(**kwargs).values(): item.update(v) else: item = self.as_dict(**kwargs) - if mode != 'object': - item = item.values() - if have_serializers: return serializers.json(item, default=default or From 97b22438fac66e6017d7d2abd1936d357f2a186d Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sat, 12 Jan 2013 14:12:19 -0600 Subject: [PATCH 47/75] fixed issue 1271, admin/languages/ru.py, thanks Dmitry.Mosin --- VERSION | 2 +- applications/admin/languages/ru.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 916dfd8f..ffa0a317 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.12.14.08.26 +Version 2.4.1-alpha.2+timestamp.2013.01.12.14.11.35 diff --git a/applications/admin/languages/ru.py b/applications/admin/languages/ru.py index ed4cb893..0dbd7022 100644 --- a/applications/admin/languages/ru.py +++ b/applications/admin/languages/ru.py @@ -92,7 +92,7 @@ 'Database': 'База данных', 'database': 'база данных', 'database %s select': 'Выбор базы данных %s ', -'database administration': 'администраторирование базы данных', +'database administration': 'администрирование базы данных', 'Date and Time': 'Дата и время', 'db': 'бд', 'DB Model': 'Модель БД', From 5ff925d5837f9caacef483c4f6609dc64c79f084 Mon Sep 17 00:00:00 2001 From: TierraDelFuego Date: Sat, 12 Jan 2013 22:53:43 -0800 Subject: [PATCH 48/75] Removing call to python where it is not needed. --- options_std.py | 1 - router.example.py | 1 - routes.example.py | 1 - 3 files changed, 3 deletions(-) diff --git a/options_std.py b/options_std.py index c531e7f3..8e0d3d6e 100644 --- a/options_std.py +++ b/options_std.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # when web2py is run as a windows service (web2py.py -W) diff --git a/router.example.py b/router.example.py index 75e3af9e..714a5a0a 100644 --- a/router.example.py +++ b/router.example.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # routers are dictionaries of URL routing parameters. diff --git a/routes.example.py b/routes.example.py index fdcfebc6..06ac5db2 100644 --- a/routes.example.py +++ b/routes.example.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # default_application, default_controller, default_function From e363ee0994b4464530a36b724f962b78be9b5e0c Mon Sep 17 00:00:00 2001 From: TierraDelFuego Date: Sat, 12 Jan 2013 23:34:35 -0800 Subject: [PATCH 49/75] Change the call to python from /usr/bin/python to /usr/bin/env python so the correct python is called when using virtualenv. Remove the the call if not needed. --- .gitignore | 3 +-- applications/welcome/routes.example.py | 1 - gluon/contrib/pysimplesoap/client.py | 2 +- gluon/contrib/pysimplesoap/server.py | 2 +- gluon/contrib/pysimplesoap/simplexml.py | 2 +- gluon/contrib/pysimplesoap/transport.py | 2 +- gluon/contrib/shell.py | 1 - gluon/contrib/simplejsonrpc.py | 1 - gluon/contrib/websocket_messaging.py | 2 +- scripts/autoroutes.py | 1 - scripts/extract_sqlite_models.py | 1 - 11 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 39a6f517..dcbca4c0 100644 --- a/.gitignore +++ b/.gitignore @@ -52,5 +52,4 @@ applications/*/*.py[oc] applications/examples/static/epydoc applications/examples/static/sphinx applications/admin/cron/cron.master - - +./HOWTO-web2py-devel diff --git a/applications/welcome/routes.example.py b/applications/welcome/routes.example.py index 5194cb8e..1f3b78cf 100644 --- a/applications/welcome/routes.example.py +++ b/applications/welcome/routes.example.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # This is an app-specific example router diff --git a/gluon/contrib/pysimplesoap/client.py b/gluon/contrib/pysimplesoap/client.py index 4b05d3a8..75bca191 100755 --- a/gluon/contrib/pysimplesoap/client.py +++ b/gluon/contrib/pysimplesoap/client.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: latin-1 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by the diff --git a/gluon/contrib/pysimplesoap/server.py b/gluon/contrib/pysimplesoap/server.py index 12c618c4..3888b49c 100755 --- a/gluon/contrib/pysimplesoap/server.py +++ b/gluon/contrib/pysimplesoap/server.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: latin-1 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by the diff --git a/gluon/contrib/pysimplesoap/simplexml.py b/gluon/contrib/pysimplesoap/simplexml.py index 6b0cea5f..2a57c902 100755 --- a/gluon/contrib/pysimplesoap/simplexml.py +++ b/gluon/contrib/pysimplesoap/simplexml.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: latin-1 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by the diff --git a/gluon/contrib/pysimplesoap/transport.py b/gluon/contrib/pysimplesoap/transport.py index a1491334..5441e342 100644 --- a/gluon/contrib/pysimplesoap/transport.py +++ b/gluon/contrib/pysimplesoap/transport.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- coding: latin-1 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by the diff --git a/gluon/contrib/shell.py b/gluon/contrib/shell.py index d6dedf50..e875a57a 100755 --- a/gluon/contrib/shell.py +++ b/gluon/contrib/shell.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # # Copyright 2007 Google Inc. # diff --git a/gluon/contrib/simplejsonrpc.py b/gluon/contrib/simplejsonrpc.py index 5143cea7..afdaec7d 100644 --- a/gluon/contrib/simplejsonrpc.py +++ b/gluon/contrib/simplejsonrpc.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by the diff --git a/gluon/contrib/websocket_messaging.py b/gluon/contrib/websocket_messaging.py index dae2feb3..dcd4a35e 100644 --- a/gluon/contrib/websocket_messaging.py +++ b/gluon/contrib/websocket_messaging.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python """ This file is part of the web2py Web Framework Copyrighted by Massimo Di Pierro diff --git a/scripts/autoroutes.py b/scripts/autoroutes.py index aaef82dd..cc68b490 100644 --- a/scripts/autoroutes.py +++ b/scripts/autoroutes.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- ''' autoroutes writes routes for you based on a simpler routing diff --git a/scripts/extract_sqlite_models.py b/scripts/extract_sqlite_models.py index 3f7ad2a1..b9638ede 100644 --- a/scripts/extract_sqlite_models.py +++ b/scripts/extract_sqlite_models.py @@ -1,4 +1,3 @@ -#!/usr/bin/python # -*- coding: utf-8 -*- ''' From 9b9b79ed588ff6891ec98157a5d3bde0fbba6e79 Mon Sep 17 00:00:00 2001 From: TierraDelFuego Date: Sat, 12 Jan 2013 23:41:28 -0800 Subject: [PATCH 50/75] Add a file to ignore. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index dcbca4c0..6525ee59 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,4 @@ applications/*/*.py[oc] applications/examples/static/epydoc applications/examples/static/sphinx applications/admin/cron/cron.master -./HOWTO-web2py-devel +HOWTO-web2py-devel From 63a049d336ae98bbf55bd78fe1c2201379330c33 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 13 Jan 2013 10:44:07 -0600 Subject: [PATCH 51/75] admin/languages/sr-*.py thanks Sasa Kelecevic --- VERSION | 2 +- applications/admin/languages/sr-cr.py | 212 ++++++++++++++++++++++++++ applications/admin/languages/sr-lt.py | 212 ++++++++++++++++++++++++++ 3 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 applications/admin/languages/sr-cr.py create mode 100644 applications/admin/languages/sr-lt.py diff --git a/VERSION b/VERSION index ffa0a317..71e0c806 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.12.14.11.35 +Version 2.4.1-alpha.2+timestamp.2013.01.13.10.43.10 diff --git a/applications/admin/languages/sr-cr.py b/applications/admin/languages/sr-cr.py new file mode 100644 index 00000000..a0acdbd9 --- /dev/null +++ b/applications/admin/languages/sr-cr.py @@ -0,0 +1,212 @@ +# coding: utf8 +{ +'!langcode!': 'sr-cr', +'!langname!': 'Српски (Ћирилица)', +'%Y-%m-%d': '%d-%m-%Y', +'%Y-%m-%d %H:%M:%S': '%d-%m-%Y %H:%M:%S', +'(requires internet access)': '(захтијева приступ интернету)', +'(something like "it-it")': '(нешто као "it-it")', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(датотека **gluon/contrib/plural_rules/%s.py** није пронађена)', +'About': 'Информације', +'About application': 'О апликацији', +'Additional code for your application': 'Додатни код за апликацију', +'admin disabled because unable to access password file': 'администрација онемогућена јер не могу приступити датотеци са лозинком', +'Admin language': 'Језик администратора', +'administrative interface': 'административни интерфејс', +'Administrator Password:': 'Лозинка администратора:', +'and rename it:': 'и преименуј у:', +'Application name:': 'Назив апликације:', +'are not used': 'није кориштено', +'are not used yet': 'није још кориштено', +'Are you sure you want to delete this object?': 'Да ли сте сигурни да желите обрисати?', +'arguments': 'arguments', +'at char %s': 'код слова %s', +'at line %s': 'на линији %s', +'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', +'back': 'назад', +'Basics': 'Основе', +'Begin': 'Почетак', +'cache, errors and sessions cleaned': 'кеш, грешке и сесије су обрисани', +'can be a git repo': 'може бити git repo', +'cannot upload file "%(filename)s"': 'не мофу отпремити датотеку "%(filename)s"', +'Change admin password': 'Промијени лзинку администратора', +'check all': 'check all', +'Check for upgrades': 'Провјери могућност надоградње', +'Checking for upgrades...': 'Провјеравам могућност надоградње...', +'Clean': 'Прочисти', +'Click row to expand traceback': 'Click row to expand traceback', +'code': 'код', +'collapse/expand all': 'сакрити/приказати све', +'Compile': 'Компајлирај', +'Controllers': 'Контролери', +'controllers': 'контролери', +'Count': 'Count', +'Create': 'Креирај', +'create file with filename:': 'Креирај датотеку под називом:', +'Create rules': 'Креирај правила', +'created by': 'израдио', +'crontab': 'crontab', +'currently running': 'тренутно покренут', +'currently saved or': 'тренутно сачувано или', +'database administration': 'администрација базе података', +'Debug': 'Debug', +'defines tables': 'дефинише табеле', +'delete': 'обриши', +'Delete': 'Обриши', +'delete all checked': 'delete all checked', +'Delete this file (you will be asked to confirm deletion)': 'Обриши ову даатотеку (бићете упитани за потврду брисања)', +'Deploy': 'Постави', +'Deploy on Google App Engine': 'Постави на Google App Engine', +'Deploy to OpenShift': 'Постави на OpenShift', +'Detailed traceback description': 'Detailed traceback description', +'direction: ltr': 'direction: ltr', +'Disable': 'Искључи', +'docs': 'документација', +'download layouts': 'преузми layouts', +'download plugins': 'преузми plugins', +'Edit': 'Уређивање', +'edit all': 'уреди све', +'Edit application': 'Уреди апликацију', +'edit controller': 'уреди контролер', +'edit views:': 'уреди views:', +'Editing file "%s"': 'Уређивање датотеке "%s"', +'Editing Language file': 'Уређивање језичке датотеке', +'Error': 'Грешка', +'Error logs for "%(app)s"': 'Преглед грешака за "%(app)s"', +'Error snapshot': 'Error snapshot', +'Error ticket': 'Error ticket', +'Errors': 'Грешке', +'Exception instance attributes': 'Exception instance attributes', +'Expand Abbreviation': 'Expand Abbreviation', +'exposes': 'exposes', +'exposes:': 'exposes:', +'extends': 'проширује', +'failed to compile file because:': 'нисам могао да компајлирам због:', +'File': 'Датотека', +'file does not exist': 'датотека не постоји', +'file saved on %s': 'датотека сачувана на %s', +'filter': 'филтер', +'Find Next': 'Пронађи сљедећи', +'Find Previous': 'Пронађи претходни', +'Frames': 'Frames', +'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', +'Generate': 'Generate', +'Get from URL:': 'Преузми са странице:', +'Git Pull': 'Git Pull', +'Git Push': 'Git Push', +'Go to Matching Pair': 'Go to Matching Pair', +'go!': 'крени!', +'Help': 'Помоћ', +'Hide/Show Translated strings': 'Сакрити/Приказати преведене ријечи', +'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.', +'includes': 'укључује', +'inspect attributes': 'inspect attributes', +'Install': 'Инсталирај', +'Installed applications': 'Инсталиране апликације', +'invalid password.': 'погрешна лозинка.', +'Key bindings': 'Пречице', +'Key bindings for ZenCoding Plugin': 'Пречице за ZenCoding Plugin', +'Language files (static strings) updated': 'Језичке датотеке су ажуриране', +'languages': 'језици', +'Languages': 'Језици', +'Last saved on:': 'Посљедња измјена:', +'License for': 'Лиценца за', +'loading...': 'преузимам...', +'locals': 'locals', +'Login': 'Пријава', +'Login to the Administrative Interface': 'Пријава за административни интерфејс', +'Logout': 'Излаз', +'Match Pair': 'Match Pair', +'Merge Lines': 'Споји линије', +'models': 'models', +'Models': 'Models', +'Modules': 'Modules', +'modules': 'modules', +'New Application Wizard': 'Чаробњак за нове апликације', +'New application wizard': 'Чаробњак за нове апликације', +'New simple application': 'Нова једноставна апликација', +'Next Edit Point': 'Next Edit Point', +'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder', +'online designer': 'онлајн дизајнер', +'Original/Translation': 'Оргинал/Превод', +'Overwrite installed app': 'Пребриши постојећу апликацију', +'Pack all': 'Запакуј све', +'Peeking at file': 'Peeking at file', +'Plugins': 'Plugins', +'plugins': 'plugins', +'Plural-Forms:': 'Plural-Forms:', +'Powered by': 'Омогућио', +'Previous Edit Point': 'Previous Edit Point', +'Private files': 'Private files', +'private files': 'private files', +'Project Progress': 'Напредак пројекта', +'Reload routes': 'Обнови преусмјерења', +'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', +'Replace': 'Замијени', +'Replace All': 'Замијени све', +'request': 'request', +'response': 'response', +'restart': 'restart', +'restore': 'restore', +'revert': 'revert', +'rules are not defined': 'правила нису дефинисана', +'rules:': 'правила:', +"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')", +'Running on %s': 'Покренути на %s', +'Save': 'Сачувај', +'Save via Ajax': 'сачувај via Ajax', +'Saved file hash:': 'Сачувано као хаш:', +'session': 'сесија', +'session expired': 'сесија истекла', +'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', +'shell': 'shell', +'Site': 'Сајт', +'skip to generate': 'skip to generate', +'Start a new app': 'Покрени нову апликацију', +'Start searching': 'Покрени претрагу', +'Start wizard': 'Покрени чаробњака', +'static': 'static', +'Static files': 'Static files', +'Step': 'Корак', +'Submit': 'Прихвати', +'successful': 'успјешан', +'test': 'тест', +'Testing application': 'Тестирање апликације', +'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller', +'The data representation, define database tables and sets': 'The data representation, define database tables and sets', +'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates', +'There are no models': 'There are no models', +'There are no plugins': 'There are no plugins', +'There are no private files': 'There are no private files', +'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', +'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'Ticket ID': 'Ticket ID', +'Ticket Missing': 'Ticket nedostaje', +'to previous version.': 'на претходну верзију.', +'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', +'toggle breakpoint': 'toggle breakpoint', +'Toggle Fullscreen': 'Toggle Fullscreen', +'Traceback': 'Traceback', +'Translation strings for the application': 'Ријечи у апликацији које треба превести', +'Try the mobile interface': 'Пробај мобилни интерфејс', +'try view': 'try view', +'uncheck all': 'uncheck all', +'Uninstall': 'Деинсталирај', +'update': 'ажурирај', +'update all languages': 'ажурирај све језике', +'upload': 'Отпреми', +'Upload a package:': 'Преузми пакет:', +'Upload and install packed application': 'Преузми и инсталирај запаковану апликацију', +'upload file:': 'преузми датотеку:', +'upload plugin file:': 'преузми плагин датотеку:', +'variables': 'variables', +'Version': 'Верзија', +'Version %s.%s.%s (%s) %s': 'Верзија %s.%s.%s (%s) %s', +'Versioning': 'Versioning', +'views': 'views', +'Views': 'Views', +'Web Framework': 'Web Framework', +'web2py is up to date': 'web2py је ажуран', +'web2py Recent Tweets': 'web2py Recent Tweets', +'Wrap with Abbreviation': 'Wrap with Abbreviation', +} diff --git a/applications/admin/languages/sr-lt.py b/applications/admin/languages/sr-lt.py new file mode 100644 index 00000000..7ab1bda6 --- /dev/null +++ b/applications/admin/languages/sr-lt.py @@ -0,0 +1,212 @@ +# coding: utf8 +{ +'!langcode!': 'sr-lt', +'!langname!': 'Srpski (Latinica)', +'%Y-%m-%d': '%d-%m-%Y', +'%Y-%m-%d %H:%M:%S': '%d-%m-%Y %H:%M:%S', +'(requires internet access)': '(zahtijeva pristup internetu)', +'(something like "it-it")': '(nešto kao "it-it")', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(datoteka **gluon/contrib/plural_rules/%s.py** nije pronađena)', +'About': 'Informacije', +'About application': 'O aplikaciji', +'Additional code for your application': 'Dodatni kod za aplikaciju', +'admin disabled because unable to access password file': 'administracija onemogućena jer ne mogu pristupiti datoteci sa lozinkom', +'Admin language': 'Jezik administratora', +'administrative interface': 'administrativni interfejs', +'Administrator Password:': 'Lozinka administratora:', +'and rename it:': 'i preimenuj u:', +'Application name:': 'Naziv aplikacije:', +'are not used': 'nije korišteno', +'are not used yet': 'nije još korišteno', +'Are you sure you want to delete this object?': 'Da li ste sigurni da želite obrisati?', +'arguments': 'arguments', +'at char %s': 'kod slova %s', +'at line %s': 'na liniji %s', +'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.', +'back': 'nazad', +'Basics': 'Osnove', +'Begin': 'Početak', +'cache, errors and sessions cleaned': 'keš, greške i sesije su obrisani', +'can be a git repo': 'može biti git repo', +'cannot upload file "%(filename)s"': 'ne mogu otpremiti datoteku "%(filename)s"', +'Change admin password': 'Promijeni lozinku administratora', +'check all': 'check all', +'Check for upgrades': 'Provjeri mogućnost nadogradnje', +'Checking for upgrades...': 'Provjeravam mogućnost nadogradnje...', +'Clean': 'Pročisti', +'Click row to expand traceback': 'Click row to expand traceback', +'code': 'kod', +'collapse/expand all': 'sakriti/prikazati sve', +'Compile': 'Kompajliraj', +'Controllers': 'Kontroleri', +'controllers': 'kontroleri', +'Count': 'Count', +'Create': 'Kreiraj', +'create file with filename:': 'Kreiraj datoteku pod nazivom:', +'Create rules': 'Kreiraj pravila', +'created by': 'izradio', +'crontab': 'crontab', +'currently running': 'trenutno pokrenut', +'currently saved or': 'trenutno sačuvano ili', +'database administration': 'administracija baze podataka', +'Debug': 'Debug', +'defines tables': 'definiše tabele', +'delete': 'obriši', +'Delete': 'Obriši', +'delete all checked': 'delete all checked', +'Delete this file (you will be asked to confirm deletion)': 'Obriši ovu datoteku (bićete upitani za potvrdu brisanja)', +'Deploy': 'Postavi', +'Deploy on Google App Engine': 'Postavi na Google App Engine', +'Deploy to OpenShift': 'Postavi na OpenShift', +'Detailed traceback description': 'Detailed traceback description', +'direction: ltr': 'direction: ltr', +'Disable': 'Isključi', +'docs': 'dokumentacija', +'download layouts': 'preuzmi layouts', +'download plugins': 'preuzmi plugins', +'Edit': 'Uređivanje', +'edit all': 'uredi sve', +'Edit application': 'Uredi aplikaciju', +'edit controller': 'uredi controller', +'edit views:': 'uredi views:', +'Editing file "%s"': 'Uređivanje datoteke "%s"', +'Editing Language file': 'Uređivanje jezičke datoteke', +'Error': 'Greška', +'Error logs for "%(app)s"': 'Pregled grešaka za "%(app)s"', +'Error snapshot': 'Error snapshot', +'Error ticket': 'Error ticket', +'Errors': 'Greške', +'Exception instance attributes': 'Exception instance attributes', +'Expand Abbreviation': 'Expand Abbreviation', +'exposes': 'exposes', +'exposes:': 'exposes:', +'extends': 'proširuje', +'failed to compile file because:': 'nisam mogao da kompajliram zbog:', +'File': 'Datoteka', +'file does not exist': 'datoteka ne postoji', +'file saved on %s': 'datoteka sačuvana na %s', +'filter': 'filter', +'Find Next': 'Pronađi sljedeći', +'Find Previous': 'Pronađi prethodni', +'Frames': 'Frames', +'Functions with no doctests will result in [passed] tests.': 'Functions with no doctests will result in [passed] tests.', +'Generate': 'Generate', +'Get from URL:': 'Preuzmi sa stranice:', +'Git Pull': 'Git Pull', +'Git Push': 'Git Push', +'Go to Matching Pair': 'Go to Matching Pair', +'go!': 'kreni!', +'Help': 'Pomoć', +'Hide/Show Translated strings': 'Sakriti/Prikazati prevedene riječi', +'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.', +'includes': 'uključuje', +'inspect attributes': 'inspect attributes', +'Install': 'Instaliraj', +'Installed applications': 'Instalirane aplikacije', +'invalid password.': 'pogrešna lozinka.', +'Key bindings': 'Prečice', +'Key bindings for ZenCoding Plugin': 'Prečice za for ZenCoding Plugin', +'Language files (static strings) updated': 'Jezičke datoteke su ažurirane', +'languages': 'jezici', +'Languages': 'Jezici', +'Last saved on:': 'Posljednja izmjena:', +'License for': 'Licenca za', +'loading...': 'preuzimam...', +'locals': 'locals', +'Login': 'Prijava', +'Login to the Administrative Interface': 'Prijava za administrativni interfejs', +'Logout': 'Izlaz', +'Match Pair': 'Match Pair', +'Merge Lines': 'Spoji linije', +'models': 'models', +'Models': 'Models', +'Modules': 'Modules', +'modules': 'modules', +'New Application Wizard': 'Čarobnjak za nove aplikacije', +'New application wizard': 'Čarobnjak za nove aplikacije', +'New simple application': 'Nova jednostavna aplikacija', +'Next Edit Point': 'Next Edit Point', +'No ticket_storage.txt found under /private folder': 'No ticket_storage.txt found under /private folder', +'online designer': 'onlajn dizajner', +'Original/Translation': 'Original/Prevod', +'Overwrite installed app': 'Prebriši postojeću aplikaciju', +'Pack all': 'Zapakuj sve', +'Peeking at file': 'Peeking at file', +'Plugins': 'Plugins', +'plugins': 'plugins', +'Plural-Forms:': 'Plural-Forms:', +'Powered by': 'Omogućio', +'Previous Edit Point': 'Previous Edit Point', +'Private files': 'Private files', +'private files': 'private files', +'Project Progress': 'Napredak projekta', +'Reload routes': 'Obnovi preusmjerenja', +'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', +'Replace': 'Zamijeni', +'Replace All': 'Zamijeni sve', +'request': 'request', +'response': 'response', +'restart': 'restart', +'restore': 'restore', +'revert': 'revert', +'rules are not defined': 'pravila nisu definisana', +'rules:': 'pravila:', +"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')", +'Running on %s': 'Pokrenuto na %s', +'Save': 'Sačuvaj', +'Save via Ajax': 'Sačuvaj via Ajax', +'Saved file hash:': 'Sačuvano kao haš:', +'session': 'sesija', +'session expired': 'sesija istekla', +'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', +'shell': 'shell', +'Site': 'Sajt', +'skip to generate': 'skip to generate', +'Start a new app': 'Pokreni novu aplikaciju', +'Start searching': 'Pokreni pretragu', +'Start wizard': 'Pokreni čarobnjaka', +'static': 'static', +'Static files': 'Static files', +'Step': 'Korak', +'Submit': 'Prihvati', +'successful': 'uspješan', +'test': 'test', +'Testing application': 'Testing application', +'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller', +'The data representation, define database tables and sets': 'The data representation, define database tables and sets', +'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates', +'There are no models': 'There are no models', +'There are no plugins': 'There are no plugins', +'There are no private files': 'There are no private files', +'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', +'These files are served without processing, your images go here': 'These files are served without processing, your images go here', +'Ticket ID': 'Ticket ID', +'Ticket Missing': 'Ticket nedostaje', +'to previous version.': 'na prethodnu verziju.', +'To create a plugin, name a file/folder plugin_[name]': 'To create a plugin, name a file/folder plugin_[name]', +'toggle breakpoint': 'toggle breakpoint', +'Toggle Fullscreen': 'Toggle Fullscreen', +'Traceback': 'Traceback', +'Translation strings for the application': 'Riječi u aplikaciji koje treba prevesti', +'Try the mobile interface': 'Probaj mobilni interfejs', +'try view': 'try view', +'uncheck all': 'uncheck all', +'Uninstall': 'Deinstaliraj', +'update': 'ažuriraj', +'update all languages': 'ažuriraj sve jezike', +'upload': 'Otpremi', +'Upload a package:': 'Preuzmi paket:', +'Upload and install packed application': 'Preuzmi i instaliraj zapakovanu aplikaciju', +'upload file:': 'preuzmi datoteku:', +'upload plugin file:': 'preuzmi plugin datoteku:', +'variables': 'variables', +'Version': 'Verzija', +'Version %s.%s.%s (%s) %s': 'Verzija %s.%s.%s (%s) %s', +'Versioning': 'Versioning', +'views': 'views', +'Views': 'Views', +'Web Framework': 'Web Framework', +'web2py is up to date': 'web2py je ažuran', +'web2py Recent Tweets': 'web2py Recent Tweets', +'Wrap with Abbreviation': 'Wrap with Abbreviation', +} From b49de3eccb5b5733a693fb4ee8707693a446e596 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 13 Jan 2013 13:08:41 -0600 Subject: [PATCH 52/75] Row.as_xml and Rows.as_csv, issue 1275, thank you Alan --- VERSION | 2 +- gluon/dal.py | 146 +++++++++++++++++++++++++++++---------------------- 2 files changed, 84 insertions(+), 64 deletions(-) diff --git a/VERSION b/VERSION index 71e0c806..ca03c054 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.13.10.43.10 +Version 2.4.1-alpha.2+timestamp.2013.01.13.13.07.43 diff --git a/gluon/dal.py b/gluon/dal.py index 0d7ecfef..0f8b3967 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6689,32 +6689,87 @@ class Row(object): del d[k] return d - def as_json(self, default=None, **kwargs): + def as_xml(self, row_name="row", colnames=None, indent=' '): + def f(row,field,indent=' '): + if isinstance(row,Row): + spc = indent+' \n' + items = [f(row[x],x,indent+' ') for x in row] + return '%s<%s>\n%s\n%s' % ( + indent, + field, + spc.join(item for item in items if item), + indent, + field) + elif not callable(row): + if REGEX_ALPHANUMERIC.match(field): + return '%s<%s>%s' % (indent,field,row,field) + else: + return '%s%s' % \ + (indent,field,row) + else: + return None + return f(self, row_name, indent=indent) + + def as_json(self, mode="object", default=None, colnames=None, + serialize=True, **kwargs): """ serializes the table to a JSON list of objects kwargs are passed to .as_dict method - only "object" mode supported + only "object" mode supported for single row + + serialize = False used by Rows.as_json TODO: return array mode with query column order """ + + def inner_loop(record, col): + (t, f) = col.split('.') + res = None + if not REGEX_TABLE_DOT_FIELD.match(col): + key = col + res = record._extra[col] + else: + key = f + if isinstance(record.get(t, None), Row): + res = record[t][f] + else: + res = record[f] + if mode == 'object': + return (key, res) + else: + return res + multi = any([isinstance(v, self.__class__) for v in self.values()]) - item = dict() + mode = mode.lower() + if not mode in ['object', 'array']: + raise SyntaxError('Invalid JSON serialization mode: %s' % mode) - if multi: - for v in self.as_dict(**kwargs).values(): - item.update(v) + if mode=='object' and colnames: + item = dict([inner_loop(self, col) for col in colnames]) + elif colnames: + item = [inner_loop(self, col) for col in colnames] else: - item = self.as_dict(**kwargs) + if not mode == 'object': + raise SyntaxError('Invalid JSON serialization mode: %s' % mode) - if have_serializers: - return serializers.json(item, - default=default or - serializers.custom_json) + if multi: + item = dict() + [item.update(**v.as_dict(**kwargs)) for v in self.values()] + else: + item = self.as_dict(**kwargs) + + if serialize: + if have_serializers: + return serializers.json(item, + default=default or + serializers.custom_json) + else: + try: + import json as simplejson + except ImportError: + import gluon.contrib.simplejson as simplejson + return simplejson.dumps(item) else: - try: - import json as simplejson - except ImportError: - import gluon.contrib.simplejson as simplejson - return simplejson.dumps(item) + return item ################################################################################ @@ -9592,30 +9647,14 @@ class Rows(object): """ serializes the table using sqlhtml.SQLTABLE (if present) """ + if strict: ncols = len(self.colnames) - def f(row,field,indent=' '): - if isinstance(row,Row): - spc = indent+' \n' - items = [f(row[x],x,indent+' ') for x in row] - return '%s<%s>\n%s\n%s' % ( - indent, - field, - spc.join(item for item in items if item), - indent, - field) - elif not callable(row): - if REGEX_ALPHANUMERIC.match(field): - return '%s<%s>%s' % (indent,field,row,field) - else: - return '%s%s' % \ - (indent,field,row) - else: - return None - return '<%s>\n%s\n' % ( - rows_name, - '\n'.join(f(row,row_name) for row in self), - rows_name) + return '<%s>\n%s\n' % (rows_name, + '\n'.join(row.as_xml(row_name=row_name, + colnames=self.colnames) for + row in self), rows_name) + import sqlhtml return sqlhtml.SQLTABLE(self).xml() @@ -9626,33 +9665,12 @@ class Rows(object): """ serializes the table to a JSON list of objects """ - mode = mode.lower() - if not mode in ['object', 'array']: - raise SyntaxError('Invalid JSON serialization mode: %s' % mode) - def inner_loop(record, col): - (t, f) = col.split('.') - res = None - if not REGEX_TABLE_DOT_FIELD.match(col): - key = col - res = record._extra[col] - else: - key = f - if isinstance(record.get(t, None), Row): - res = record[t][f] - else: - res = record[f] - if mode == 'object': - return (key, res) - else: - return res + items = [record.as_json(mode=mode, default=default, + serialize=False, + colnames=self.colnames) for + record in self] - if mode == 'object': - items = [dict([inner_loop(record, col) for col in - self.colnames]) for record in self] - else: - items = [[inner_loop(record, col) for col in self.colnames] - for record in self] if have_serializers: return serializers.json(items, default=default or @@ -9664,6 +9682,8 @@ class Rows(object): import gluon.contrib.simplejson as simplejson return simplejson.dumps(items) + # for consistent naming yet backwards compatible + as_csv = __str__ json = as_json ################################################################################ From 4bbe6ed19520a98eaabae42074a7b1d58b3a47ba Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 13 Jan 2013 13:15:38 -0600 Subject: [PATCH 53/75] minor fix in parse_as_rest --- VERSION | 2 +- gluon/dal.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ca03c054..5dfa5b06 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.13.13.07.43 +Version 2.4.1-alpha.2+timestamp.2013.01.13.13.14.47 diff --git a/gluon/dal.py b/gluon/dal.py index 0f8b3967..5cdd8adb 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7273,6 +7273,8 @@ def index(): i = 0 while i Date: Mon, 14 Jan 2013 09:18:43 -0600 Subject: [PATCH 54/75] parse_as_rest patch, thanks Denes --- VERSION | 2 +- gluon/dal.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 5dfa5b06..0833870b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.13.13.14.47 +Version 2.4.1-alpha.2+timestamp.2013.01.14.09.18.01 diff --git a/gluon/dal.py b/gluon/dal.py index 5cdd8adb..8e58674c 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7351,15 +7351,19 @@ def index(): ref = tag[tag.find('[')+1:-1] if '.' in ref and otable: table,field = ref.split('.') - # print table,field + selfld = '_id' + if db[table][field].type.startswith('reference '): + refs = [ x.name for x in db[otable] if x.type == db[table][field].type ] + if refs: + selfld = refs[0] if nested_select: try: - dbset=db(db[table][field].belongs(dbset._select(db[otable]._id))) + dbset=db(db[table][field].belongs(dbset._select(db[otable][selfld]))) except ValueError: return Row({'status':400,'pattern':pattern, 'error':'invalid path','response':None}) else: - items = [item.id for item in dbset.select(db[otable]._id)] + items = [item.id for item in dbset.select(db[otable][selfld])] dbset=db(db[table][field].belongs(items)) else: table = ref From cb2c5aae48eb9531e3cee927257af9ceebcb5a9c Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 14 Jan 2013 09:42:59 -0600 Subject: [PATCH 55/75] fixed issue 1273, default int as_dict, thanks Alan --- VERSION | 2 +- gluon/dal.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 0833870b..83c8712d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.14.09.18.01 +Version 2.4.1-alpha.2+timestamp.2013.01.14.09.41.53 diff --git a/gluon/dal.py b/gluon/dal.py index 8e58674c..b754468f 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -9577,6 +9577,22 @@ class Rows(object): :param storage_to_dict: when True returns a dict, otherwise a list(default True) :param datetime_to_str: convert datetime fields as strings (default True) """ + + # test for multiple rows + multi = False + f = self.first() + if f: + multi = any([isinstance(v, f.__class__) for v in f.values()]) + if (not "." in key) and multi: + # No key provided, default to int indices + def new_key(): + i = 0 + while True: + yield i + i += 1 + key_generator = new_key() + key = lambda r: key_generator.next() + rows = self.as_list(compact, storage_to_dict, datetime_to_str, custom_types) if isinstance(key,str) and key.count('.')==1: (table, field) = key.split('.') From d05aceb3f34bf6b8f785c91c78e58e54a3625d55 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 14 Jan 2013 09:44:47 -0600 Subject: [PATCH 56/75] fixed issue 1276, preserving subject encoding in IMAP adapter, thanks Alan --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 83c8712d..25d7f643 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.14.09.41.53 +Version 2.4.1-alpha.2+timestamp.2013.01.14.09.43.37 From 41f5dfaffccd388933540a7e191da0b9bf87d56f Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 14 Jan 2013 15:26:46 -0600 Subject: [PATCH 57/75] gluon/scheduler.py --- VERSION | 2 +- gluon/scheduler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 25d7f643..6c991fa7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.14.09.43.37 +Version 2.4.1-alpha.2+timestamp.2013.01.14.15.25.58 diff --git a/gluon/scheduler.py b/gluon/scheduler.py index 57a2937a..a3da2d35 100644 --- a/gluon/scheduler.py +++ b/gluon/scheduler.py @@ -579,7 +579,7 @@ class Scheduler(MetaScheduler): break except: db.rollback() - logger.error('TICKER(%s): error popping tasks', self.worker_name) + logger.error('%s: error popping tasks', self.worker_name) x += 1 time.sleep(0.5) From 918971cab88e01b46dae2cf330ca4c3ce760e097 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Mon, 14 Jan 2013 15:30:01 -0600 Subject: [PATCH 58/75] fixing issue 1276 again, thanks Alan --- VERSION | 2 +- gluon/dal.py | 49 ++++++++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/VERSION b/VERSION index 6c991fa7..479e2215 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.14.15.25.58 +Version 2.4.1-alpha.2+timestamp.2013.01.14.15.29.20 diff --git a/gluon/dal.py b/gluon/dal.py index b754468f..a6bb48fb 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -5841,16 +5841,22 @@ class IMAPAdapter(NoSQLAdapter): else: return None + @staticmethod + def header_represent(f, r): + from email.header import decode_header + text, encoding = decode_header(f)[0] + return text + def encode_text(self, text, charset, errors="replace"): """ convert text for mail to unicode""" if text is None: text = "" else: if isinstance(text, str): - if charset is not None: - text = unicode(text, charset, errors) - else: + if charset is None: text = unicode(text, "utf-8", errors) + else: + text = unicode(text, charset, errors) else: raise Exception("Unsupported mail text type %s" % type(text)) return text.encode("utf-8") @@ -5952,6 +5958,7 @@ class IMAPAdapter(NoSQLAdapter): Field("mime", "string", writable=False), Field("email", "string", writable=False, readable=False), Field("attachments", "list:string", writable=False, readable=False), + Field("encoding") # main charset detected ) # Set a special _mailbox attribute for storing @@ -5959,6 +5966,11 @@ class IMAPAdapter(NoSQLAdapter): self.db[name].mailbox = \ self.connection.mailbox_names[name] + # decode quoted printable + self.db[name].to.represent = self.db[name].cc.represent = \ + self.db[name].bcc.represent = self.db[name].sender.represent = \ + self.db[name].subject.represent = self.header_represent + # Set the db instance mailbox collections self.db.mailboxes = self.connection.mailbox_names return self.db.mailboxes @@ -5981,12 +5993,11 @@ class IMAPAdapter(NoSQLAdapter): query = self.common_filter(query, [self.get_query_mailbox(query),]) import email - import email.header - decode_header = email.header.decode_header # get records from imap server with search + fetch # convert results to a dictionary tablename = None fetch_results = list() + if isinstance(query, Query): tablename = self.get_table(query) mailbox = self.connection.mailbox_names.get(tablename, None) @@ -6087,6 +6098,8 @@ class IMAPAdapter(NoSQLAdapter): # pending: search flags states trough the email message # instances for correct output + # preserve subject encoding (ASCII/quoted printable) + if "%s.id" % tablename in fieldnames: item_dict["%s.id" % tablename] = n if "%s.created" % tablename in fieldnames: @@ -6097,17 +6110,17 @@ class IMAPAdapter(NoSQLAdapter): # If there is no encoding found in the message header # force utf-8 replacing characters (change this to # module's defaults). Applies to .sender, .to, .cc and .bcc fields - item_dict["%s.sender" % tablename] = self.encode_text(message["From"], charset) + item_dict["%s.sender" % tablename] = message["From"] if "%s.to" % tablename in fieldnames: - item_dict["%s.to" % tablename] = self.encode_text(message["To"], charset) + item_dict["%s.to" % tablename] = message["To"] if "%s.cc" % tablename in fieldnames: if "Cc" in message.keys(): - item_dict["%s.cc" % tablename] = self.encode_text(message["Cc"], charset) + item_dict["%s.cc" % tablename] = message["Cc"] else: item_dict["%s.cc" % tablename] = "" if "%s.bcc" % tablename in fieldnames: if "Bcc" in message.keys(): - item_dict["%s.bcc" % tablename] = self.encode_text(message["Bcc"], charset) + item_dict["%s.bcc" % tablename] = message["Bcc"] else: item_dict["%s.bcc" % tablename] = "" if "%s.deleted" % tablename in fieldnames: @@ -6121,17 +6134,13 @@ class IMAPAdapter(NoSQLAdapter): if "%s.seen" % tablename in fieldnames: item_dict["%s.seen" % tablename] = "\\Seen" in flags if "%s.subject" % tablename in fieldnames: - subject = message["Subject"] - decoded_subject = decode_header(subject) - text = decoded_subject[0][0] - encoding = decoded_subject[0][1] - if encoding in (None, ""): - encoding = charset - item_dict["%s.subject" % tablename] = self.encode_text(text, encoding) + item_dict["%s.subject" % tablename] = message["Subject"] if "%s.answered" % tablename in fieldnames: item_dict["%s.answered" % tablename] = "\\Answered" in flags if "%s.mime" % tablename in fieldnames: item_dict["%s.mime" % tablename] = message.get_content_type() + if "%s.encoding" % tablename in fieldnames: + item_dict["%s.encoding" % tablename] = charset # Here goes the whole RFC822 body as an email instance # for controller side custom processing @@ -6139,7 +6148,8 @@ class IMAPAdapter(NoSQLAdapter): # >> email.message_from_string(raw string) # returns a Message object for enhanced object processing if "%s.email" % tablename in fieldnames: - item_dict["%s.email" % tablename] = self.encode_text(raw_message, charset) + # WARNING: no encoding performed (raw message) + item_dict["%s.email" % tablename] = raw_message # Size measure as suggested in a Velocity Reviews post # by Tim Williams: "how to get size of email attachment" @@ -6152,8 +6162,9 @@ class IMAPAdapter(NoSQLAdapter): attachments.append(part.get_payload(decode=True)) if "%s.content" % tablename in fieldnames: if "text" in part.get_content_maintype(): - payload = self.encode_text(part.get_payload(decode=True), charset) - content.append(payload) + part_charset = self.get_charset(part) + payload = part.get_payload(decode=True) + content.append(self.encode_text(payload, part_charset)) if "%s.size" % tablename in fieldnames: if part is not None: size += len(str(part)) From cf5f1c9c6e69680c8b78b96bb4af52821192312f Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 15 Jan 2013 13:08:27 -0600 Subject: [PATCH 59/75] fixed git errors issues, thanks Alan, wonderful work --- VERSION | 2 +- applications/admin/controllers/default.py | 63 +++++++++++++---------- 2 files changed, 38 insertions(+), 27 deletions(-) diff --git a/VERSION b/VERSION index 479e2215..7a22c5a4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.14.15.29.20 +Version 2.4.1-alpha.2+timestamp.2013.01.15.13.07.36 diff --git a/applications/admin/controllers/default.py b/applications/admin/controllers/default.py index ba67a9f5..502ab404 100644 --- a/applications/admin/controllers/default.py +++ b/applications/admin/controllers/default.py @@ -15,7 +15,13 @@ from glob import glob import shutil import platform try: - from git import * + import git + GIT_ERRORS = (git.GitCommandError, git.InvalidGitRepositoryError, + git.NoSuchPathError) + if git.__version__ >= '0.3.1': + GIT_ERRORS += (git.CacheError, git.CheckoutError, + git.ODBError, git.ParseError, + git.UnmergedEntriesError) have_git = True except ImportError: have_git = False @@ -240,10 +246,10 @@ def site(): redirect(URL(r=request)) target = os.path.join(apath(r=request), form_update.vars.name) try: - new_repo = Repo.clone_from(form_update.vars.url, target) + new_repo = git.Repo.clone_from(form_update.vars.url, target) session.flash = T('new application "%s" imported', form_update.vars.name) - except GitCommandError, err: + except git.GitCommandError, err: session.flash = T('Invalid git repository specified.') redirect(URL(r=request)) @@ -1726,29 +1732,31 @@ def git_pull(): {T('Cancel'): URL('site')}) if dialog.accepted: try: - repo = Repo(os.path.join(apath(r=request), app)) + repo = git.Repo(os.path.join(apath(r=request), app)) origin = repo.remotes.origin origin.fetch() origin.pull() session.flash = T("Application updated via git pull") redirect(URL('site')) - except CheckoutError, message: - session.flash = T("Pull failed, certain files could not be checked out. Check logs for details.") - redirect(URL('site')) - except UnmergedEntriesError: - session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") - redirect(URL('site')) + except GIT_ERRORS, e: + error_type = type(e) + if 'CheckoutError' in error_type: + session.flash = T("Pull failed, certain files could not be checked out. Check logs for details.") + redirect(URL('site')) + elif 'UnmergedEntriesError' in error_type: + session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") + redirect(URL('site')) + elif 'GitCommandError' in error_type: + session.flash = T( + "Pull failed, git exited abnormally. See logs for details.") + redirect(URL('site')) + else: + session.flash = T( + "Git error: %s %s" % (error_type, str(e))) + redirect(URL('site')) except AssertionError: session.flash = T("Pull is not possible because you have unmerged files. Fix them up in the work tree, and then try again.") redirect(URL('site')) - except GitCommandError, status: - session.flash = T( - "Pull failed, git exited abnormally. See logs for details.") - redirect(URL('site')) - except Exception, e: - session.flash = T( - "Pull failed, git exited abnormally. See logs for details.") - redirect(URL('site')) elif 'cancel' in request.vars: redirect(URL('site')) return dict(app=app, dialog=dialog) @@ -1766,7 +1774,7 @@ def git_push(): form.process() if form.accepted: try: - repo = Repo(os.path.join(apath(r=request), app)) + repo = git.Repo(os.path.join(apath(r=request), app)) index = repo.index index.add([apath(r=request) + app + '/*']) new_commit = index.commit(form.vars.changelog) @@ -1775,11 +1783,14 @@ def git_push(): session.flash = T( "Git repo updated with latest application changes.") redirect(URL('site')) - except UnmergedEntriesError: - session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.") - redirect(URL('site')) - except Exception, e: - session.flash = T( - "Push failed, git exited abnormally. See logs for details.") - redirect(URL('site')) + except GIT_ERRORS, e: + error_type = type(e) + if "UnmergedEntriesError" in error_type: + session.flash = T("Push failed, there are unmerged entries in the cache. Resolve merge issues manually and try again.") + redirect(URL('site')) + else: + session.flash = T( + "Git error: %s %s" % (error_type, str(e))) + redirect(URL('site')) return dict(app=app, form=form) + From 8a2c0c320ea658789ded09bdee8aeae7ad07c500 Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 15 Jan 2013 13:09:59 -0600 Subject: [PATCH 60/75] IMAP attachments in .select(), thanks Alan --- VERSION | 2 +- gluon/dal.py | 81 ++++++++++++++++++++++++++++++---------------------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/VERSION b/VERSION index 7a22c5a4..67536eeb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.13.07.36 +Version 2.4.1-alpha.2+timestamp.2013.01.15.13.09.31 diff --git a/gluon/dal.py b/gluon/dal.py index a6bb48fb..a443db4b 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -5565,7 +5565,8 @@ class IMAPAdapter(NoSQLAdapter): subject string mime string The mime header declaration email string The complete RFC822 message** - attachments list:string Each non text decoded part as string + attachments Each non text part as dict + encoding string The main detected encoding *At the application side it is measured as the length of the RFC822 message string @@ -5957,8 +5958,8 @@ class IMAPAdapter(NoSQLAdapter): Field("subject", "string", writable=False), Field("mime", "string", writable=False), Field("email", "string", writable=False, readable=False), - Field("attachments", "list:string", writable=False, readable=False), - Field("encoding") # main charset detected + Field("attachments", list, writable=False, readable=False), + Field("encoding") ) # Set a special _mailbox attribute for storing @@ -6070,11 +6071,11 @@ class IMAPAdapter(NoSQLAdapter): else: allfields = False if allfields: - fieldnames = ["%s.%s" % (tablename, field) for field in self.search_fields.keys()] + colnames = ["%s.%s" % (tablename, field) for field in self.search_fields.keys()] else: - fieldnames = ["%s.%s" % (tablename, field.name) for field in fields] + colnames = ["%s.%s" % (tablename, field.name) for field in fields] - for k in fieldnames: + for k in colnames: imapfields_dict[k] = k imapqry_list = list() @@ -6100,46 +6101,46 @@ class IMAPAdapter(NoSQLAdapter): # preserve subject encoding (ASCII/quoted printable) - if "%s.id" % tablename in fieldnames: + if "%s.id" % tablename in colnames: item_dict["%s.id" % tablename] = n - if "%s.created" % tablename in fieldnames: + if "%s.created" % tablename in colnames: item_dict["%s.created" % tablename] = self.convert_date(message["Date"]) - if "%s.uid" % tablename in fieldnames: + if "%s.uid" % tablename in colnames: item_dict["%s.uid" % tablename] = uid - if "%s.sender" % tablename in fieldnames: + if "%s.sender" % tablename in colnames: # If there is no encoding found in the message header # force utf-8 replacing characters (change this to # module's defaults). Applies to .sender, .to, .cc and .bcc fields item_dict["%s.sender" % tablename] = message["From"] - if "%s.to" % tablename in fieldnames: + if "%s.to" % tablename in colnames: item_dict["%s.to" % tablename] = message["To"] - if "%s.cc" % tablename in fieldnames: + if "%s.cc" % tablename in colnames: if "Cc" in message.keys(): item_dict["%s.cc" % tablename] = message["Cc"] else: item_dict["%s.cc" % tablename] = "" - if "%s.bcc" % tablename in fieldnames: + if "%s.bcc" % tablename in colnames: if "Bcc" in message.keys(): item_dict["%s.bcc" % tablename] = message["Bcc"] else: item_dict["%s.bcc" % tablename] = "" - if "%s.deleted" % tablename in fieldnames: + if "%s.deleted" % tablename in colnames: item_dict["%s.deleted" % tablename] = "\\Deleted" in flags - if "%s.draft" % tablename in fieldnames: + if "%s.draft" % tablename in colnames: item_dict["%s.draft" % tablename] = "\\Draft" in flags - if "%s.flagged" % tablename in fieldnames: + if "%s.flagged" % tablename in colnames: item_dict["%s.flagged" % tablename] = "\\Flagged" in flags - if "%s.recent" % tablename in fieldnames: + if "%s.recent" % tablename in colnames: item_dict["%s.recent" % tablename] = "\\Recent" in flags - if "%s.seen" % tablename in fieldnames: + if "%s.seen" % tablename in colnames: item_dict["%s.seen" % tablename] = "\\Seen" in flags - if "%s.subject" % tablename in fieldnames: + if "%s.subject" % tablename in colnames: item_dict["%s.subject" % tablename] = message["Subject"] - if "%s.answered" % tablename in fieldnames: + if "%s.answered" % tablename in colnames: item_dict["%s.answered" % tablename] = "\\Answered" in flags - if "%s.mime" % tablename in fieldnames: + if "%s.mime" % tablename in colnames: item_dict["%s.mime" % tablename] = message.get_content_type() - if "%s.encoding" % tablename in fieldnames: + if "%s.encoding" % tablename in colnames: item_dict["%s.encoding" % tablename] = charset # Here goes the whole RFC822 body as an email instance @@ -6147,7 +6148,7 @@ class IMAPAdapter(NoSQLAdapter): # The message is stored as a raw string # >> email.message_from_string(raw string) # returns a Message object for enhanced object processing - if "%s.email" % tablename in fieldnames: + if "%s.email" % tablename in colnames: # WARNING: no encoding performed (raw message) item_dict["%s.email" % tablename] = raw_message @@ -6157,19 +6158,31 @@ class IMAPAdapter(NoSQLAdapter): # To retrieve the server size for representation would add a new # fetch transaction to the process for part in message.walk(): - if "%s.attachments" % tablename in fieldnames: - if not "text" in part.get_content_maintype(): - attachments.append(part.get_payload(decode=True)) - if "%s.content" % tablename in fieldnames: - if "text" in part.get_content_maintype(): - part_charset = self.get_charset(part) + maintype = part.get_content_maintype() + if ("%s.attachments" % tablename in colnames) or \ + ("%s.content" % tablename in colnames): + if "%s.attachments" % tablename in colnames: + if not ("text" in maintype): + payload = part.get_payload(decode=True) + if payload: + attachment = { + "payload": payload, + "filename": part.get_filename(), + "encoding": part.get_content_charset(), + "mime": part.get_content_type(), + "disposition": part["Content-Disposition"]} + attachments.append(attachment) + if "%s.content" % tablename in colnames: payload = part.get_payload(decode=True) - content.append(self.encode_text(payload, part_charset)) - if "%s.size" % tablename in fieldnames: + part_charset = self.get_charset(part) + if "text" in maintype: + if payload: + content.append(self.encode_text(payload, part_charset)) + if "%s.size" % tablename in colnames: if part is not None: size += len(str(part)) item_dict["%s.content" % tablename] = bar_encode(content) - item_dict["%s.attachments" % tablename] = bar_encode(attachments) + item_dict["%s.attachments" % tablename] = attachments item_dict["%s.size" % tablename] = size imapqry_list.append(item_dict) @@ -6177,12 +6190,12 @@ class IMAPAdapter(NoSQLAdapter): # creation (sends an array or lists) for item_dict in imapqry_list: imapqry_array_item = list() - for fieldname in fieldnames: + for fieldname in colnames: imapqry_array_item.append(item_dict[fieldname]) imapqry_array.append(imapqry_array_item) # parse result and return a rows object - colnames = fieldnames + colnames = colnames processor = attributes.get('processor',self.parse) return processor(imapqry_array, fields, colnames) From 10a39fd52c35474dc310aa9f7006ac639ac61cff Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 15 Jan 2013 13:19:54 -0600 Subject: [PATCH 61/75] another parse_as_rest patch, thanks Denes --- VERSION | 2 +- gluon/dal.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index 67536eeb..a56b2937 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.13.09.31 +Version 2.4.1-alpha.2+timestamp.2013.01.15.13.19.22 diff --git a/gluon/dal.py b/gluon/dal.py index a443db4b..11f47fe4 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -7378,8 +7378,10 @@ def index(): selfld = '_id' if db[table][field].type.startswith('reference '): refs = [ x.name for x in db[otable] if x.type == db[table][field].type ] - if refs: - selfld = refs[0] + else: + refs = [ x.name for x in db[table]._referenced_by if x.tablename==otable ] + if refs: + selfld = refs[0] if nested_select: try: dbset=db(db[table][field].belongs(dbset._select(db[otable][selfld]))) From ccc1de25955d558be1ca401580aa7d3fd320e6ef Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 15 Jan 2013 13:26:14 -0600 Subject: [PATCH 62/75] minor refactoring to avoid compatibility problems --- VERSION | 2 +- gluon/widget.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index a56b2937..7c49317e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.13.19.22 +Version 2.4.1-alpha.2+timestamp.2013.01.15.13.25.43 diff --git a/gluon/widget.py b/gluon/widget.py index df279372..86ee37ed 100644 --- a/gluon/widget.py +++ b/gluon/widget.py @@ -510,8 +510,7 @@ class web2pyDialog(object): if not options.taskbar: thread.start_new_thread(start_browser, - (get_url(ip, proto=proto, port=port),), - dict(startup=True)) + (get_url(ip, proto=proto, port=port), True)) self.password.configure(state='readonly') [ip.configure(state='disabled') for ip in self.ips.values()] From 92cef0b8110f4f1c5bec4c7d256da38de638bfc4 Mon Sep 17 00:00:00 2001 From: Massimo Date: Tue, 15 Jan 2013 15:54:29 -0600 Subject: [PATCH 63/75] scheduler patch, thanks Niphlod --- VERSION | 2 +- gluon/scheduler.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 7c49317e..616d55a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.13.25.43 +Version 2.4.1-alpha.2+timestamp.2013.01.15.15.53.29 diff --git a/gluon/scheduler.py b/gluon/scheduler.py index a3da2d35..a866acc5 100644 --- a/gluon/scheduler.py +++ b/gluon/scheduler.py @@ -556,7 +556,7 @@ class Scheduler(MetaScheduler): self.die() def wrapped_assign_tasks(self, db): - db.commit() # ?don't know if it's useful, let's be completely sure + db.commit() #db.commit() only for Mysql x = 0 while x < 10: try: @@ -571,6 +571,7 @@ class Scheduler(MetaScheduler): def wrapped_pop_task(self): db = self.db + db.commit() #another nifty db.commit() only for Mysql x = 0 while x < 10: try: From 8f79000d2d9e7056e57d66712a8b9d453a5a0d0a Mon Sep 17 00:00:00 2001 From: mdipierro Date: Tue, 15 Jan 2013 22:23:00 -0600 Subject: [PATCH 64/75] fixed recent bug in dal, thanks Denes --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 616d55a6..952a16a9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.15.53.29 +Version 2.4.1-alpha.2+timestamp.2013.01.15.22.22.06 diff --git a/gluon/dal.py b/gluon/dal.py index 11f47fe4..b686d6f5 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -8204,7 +8204,7 @@ class Table(object): return fields def _insert(self, **fields): - fields = self._default(fields) + fields = self._defaults(fields) return self._db._adapter._insert(self, self._listify(fields)) def insert(self, **fields): From 3a1c03894fb0b6affd47e7bff43cedf210538554 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 16 Jan 2013 09:41:36 -0600 Subject: [PATCH 65/75] json export in grid, thanks Alan --- VERSION | 2 +- gluon/sqlhtml.py | 47 +++++++++++++++++++++++------------------------ 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/VERSION b/VERSION index 952a16a9..4ed77fd9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.15.22.22.06 +Version 2.4.1-alpha.2+timestamp.2013.01.16.09.40.52 diff --git a/gluon/sqlhtml.py b/gluon/sqlhtml.py index 2f73fd6f..aa725f15 100644 --- a/gluon/sqlhtml.py +++ b/gluon/sqlhtml.py @@ -1991,6 +1991,7 @@ class SQLFORM(FORM): csv=(ExporterCSV, 'CSV'), xml=(ExporterXML, 'XML'), html=(ExporterHTML, 'HTML'), + json=(ExporterJSON, 'JSON'), tsv_with_hidden_cols= (ExporterTSV, 'TSV (Excel compatible, hidden cols)'), tsv=(ExporterTSV, 'TSV (Excel compatible)')) @@ -2938,11 +2939,10 @@ class ExporterCSV(ExportClass): def export(self): if self.rows: - return str(self.rows) + return self.rows.as_csv() else: return '' - class ExporterHTML(ExportClass): label = 'HTML' file_ext = "html" @@ -2952,18 +2952,10 @@ class ExporterHTML(ExportClass): ExportClass.__init__(self, rows) def export(self): - out = cStringIO.StringIO() - out.write('\n\n
\n') if self.rows: - colnames = [a.split('.') for a in self.rows.colnames] - for row in self.rows.records: - out.write('\n') - for col in colnames: - out.write('\n') - out.write('\n') - out.write('
' + str(row[col[0]][col[1]]) + '
\n\n') - return str(out.getvalue()) - + return self.rows.xml() + else: + return '\n\n\n
\n\n' class ExporterXML(ExportClass): label = 'XML' @@ -2974,15 +2966,22 @@ class ExporterXML(ExportClass): ExportClass.__init__(self, rows) def export(self): - out = cStringIO.StringIO() - out.write('\n') if self.rows: - colnames = [a.split('.') for a in self.rows.colnames] - for row in self.rows.records: - out.write('\n') - for col in colnames: - out.write( - '<%s>' % col + str(row[col[0]][col[1]]) + '\n' % col) - out.write('\n') - out.write('') - return str(out.getvalue()) + return self.rows.as_xml() + else: + return '' + +class ExporterJSON(ExportClass): + label = 'JSON' + file_ext = "json" + content_type = "application/json" + + def __init__(self, rows): + ExportClass.__init__(self, rows) + + def export(self): + if self.rows: + return self.rows.as_json() + else: + return 'null' + From 5f9094e7d15fb8970c83d7b67d613f1b380e5d73 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Wed, 16 Jan 2013 11:23:29 -0600 Subject: [PATCH 66/75] wiki(templates=None), thanks Alan --- VERSION | 2 +- gluon/tools.py | 46 ++++++++++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/VERSION b/VERSION index 4ed77fd9..3062913c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.16.09.40.52 +Version 2.4.1-alpha.2+timestamp.2013.01.16.11.22.49 diff --git a/gluon/tools.py b/gluon/tools.py index be485211..fec9b7cd 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -3317,14 +3317,17 @@ class Auth(object): restrict_search=False, resolve=True, extra=None, - menu_groups=None): + menu_groups=None, + templates=None): + if not hasattr(self, '_wiki'): self._wiki = Wiki(self, render=render, manage_permissions=manage_permissions, force_prefix=force_prefix, restrict_search=restrict_search, env=env, extra=extra or {}, - menu_groups=menu_groups) + menu_groups=menu_groups, + templates=templates) else: self._wiki.env.update(env or {}) # if resolve is set to True, process request as wiki call @@ -4739,7 +4742,9 @@ class Wiki(object): def __init__(self, auth, env=None, render='markmin', manage_permissions=False, force_prefix='', - restrict_search=False, extra=None, menu_groups=None): + restrict_search=False, extra=None, + menu_groups=None, templates=None): + db = auth.db self.env = env or {} self.env['component'] = Wiki.component if render == 'markmin': @@ -4757,7 +4762,10 @@ class Wiki(object): perms = self.manage_permissions = manage_permissions self.restrict_search = restrict_search self.extra = extra or {} - db = auth.db + if templates is None and not manage_permissions: + templates = db.auth_wiki.tags.contains('template')&\ + db.auth_wiki.can_read.contains('everybody') + self.templates = templates table_definitions = [ ('wiki_page', { 'args':[ @@ -4981,8 +4989,8 @@ class Wiki(object): if slug == 'wiki-menu': db.wiki_page.body.default = \ '- Menu Item > @////index\n- - Submenu > http://web2py.com' - #else: - # db.wiki_page.body.default = db(db.wiki_page.id==from_template).select(db.wiki_page.body)[0].body if int(from_template) > 0 else '## %s\n\npage content' % title_guess + else: + db.wiki_page.body.default = db(db.wiki_page.id==from_template).select(db.wiki_page.body)[0].body if int(from_template) > 0 else '## %s\n\npage content' % title_guess vars = current.request.post_vars if vars.body: vars.body = vars.body.replace('://%s' % self.host, '://HOSTNAME') @@ -5073,18 +5081,24 @@ class Wiki(object): slugs=db(db.wiki_page.id>0).select(db.wiki_page.id,db.wiki_page.slug) options=[OPTION(row.slug,_value=row.id) for row in slugs] options.insert(0, OPTION('',_value='')) - form = SQLFORM.factory(Field("slug", default=current.request.args(1) or self.force_prefix, - requires=(IS_SLUG(), - IS_NOT_IN_DB(db,db.wiki_page.slug))), - #Field("from_template", "reference wiki_page", - # requires=IS_EMPTY_OR(IS_IN_DB(db, db.wiki_page, '%(slug)s')), - # comment=current.T("Choose Template or empty for new Page")), - _class="well span6") - form.element("[type=submit]").attributes["_value"] = current.T("Create Page from Slug") + fields = [Field("slug", default=current.request.args(1) or + self.force_prefix, + requires=(IS_SLUG(), IS_NOT_IN_DB(db,db.wiki_page.slug))),] + if self.templates: + fields.append( + Field("from_template", "reference wiki_page", + requires=IS_EMPTY_OR(IS_IN_DB(db(self.templates), + '%(slug)s')), + comment=current.T( + "Choose Template or empty for new Page"))) + form = SQLFORM.factory(*fields, _class="well span6") + form.element("[type=submit]").attributes["_value"] = \ + current.T("Create Page from Slug") if form.process().accepted: - # form.vars.from_template = 0 if not form.vars.from_template else form.vars.from_template - redirect(URL(args=('_edit',form.vars.slug,form.vars.from_template or 0))) # added param + form.vars.from_template = 0 if not form.vars.from_template \ + else form.vars.from_template + redirect(URL(args=('_edit', form.vars.slug,form.vars.from_template or 0))) # added param return dict(content=form) def pages(self): From 1122ad65ecdc4f633e975d31eb9dd87d2f7f63cf Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 17 Jan 2013 11:00:56 -0600 Subject: [PATCH 67/75] codemirror 3, thnaks Roberto Perdomo and Mariano Reingart --- VERSION | 2 +- applications/admin/languages/es.py | 351 +- .../admin/static/codemirror/README.md | 5 +- .../admin/static/codemirror/emmet.min.js | 566 +- .../admin/static/codemirror/keymap/emacs.js | 3 +- .../admin/static/codemirror/keymap/vim.js | 2994 ++++++-- .../static/codemirror/lib/codemirror.css | 317 +- .../admin/static/codemirror/lib/codemirror.js | 6482 ++++++++++------- .../static/codemirror/lib/util/dialog.css | 19 +- .../static/codemirror/lib/util/dialog.js | 37 +- .../static/codemirror/lib/util/foldcode.js | 132 +- .../static/codemirror/lib/util/formatting.js | 369 +- .../codemirror/lib/util/javascript-hint.js | 41 +- .../codemirror/lib/util/match-highlighter.js | 8 +- .../static/codemirror/lib/util/multiplex.js | 28 +- .../static/codemirror/lib/util/overlay.js | 9 +- .../static/codemirror/lib/util/pig-hint.js | 12 +- .../static/codemirror/lib/util/runmode.js | 39 +- .../static/codemirror/lib/util/search.js | 11 +- .../codemirror/lib/util/searchcursor.js | 6 +- .../static/codemirror/lib/util/simple-hint.js | 31 +- .../static/codemirror/lib/util/xml-hint.js | 10 +- .../static/codemirror/mode/clike/clike.js | 32 +- .../static/codemirror/mode/clike/index.html | 1 + .../static/codemirror/mode/clike/scala.html | 1 + .../mode/coffeescript/coffeescript.js | 2 +- .../admin/static/codemirror/mode/css/css.js | 421 +- .../static/codemirror/mode/css/index.html | 2 + .../codemirror/mode/htmlmixed/htmlmixed.js | 25 +- .../codemirror/mode/javascript/index.html | 20 +- .../codemirror/mode/javascript/javascript.js | 86 +- .../static/codemirror/mode/less/index.html | 3 +- .../static/codemirror/mode/python/index.html | 1 + .../static/codemirror/mode/python/python.js | 10 +- .../admin/static/codemirror/mode/xml/xml.js | 28 +- .../static/codemirror/theme/ambiance.css | 21 +- .../static/codemirror/theme/blackboard.css | 6 +- .../admin/static/codemirror/theme/cobalt.css | 6 +- .../static/codemirror/theme/erlang-dark.css | 6 +- .../static/codemirror/theme/lesser-dark.css | 8 +- .../admin/static/codemirror/theme/monokai.css | 6 +- .../admin/static/codemirror/theme/night.css | 6 +- .../static/codemirror/theme/rubyblue.css | 6 +- .../static/codemirror/theme/vibrant-ink.css | 6 +- .../admin/static/codemirror/theme/web2py.css | 4 +- .../admin/static/codemirror/theme/xq-dark.css | 6 +- applications/admin/static/js/ajax_editor.js | 23 +- applications/admin/views/default/edit.html | 148 +- 48 files changed, 7788 insertions(+), 4568 deletions(-) diff --git a/VERSION b/VERSION index 3062913c..62353f59 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.16.11.22.49 +Version 2.4.1-alpha.2+timestamp.2013.01.17.11.00.19 diff --git a/applications/admin/languages/es.py b/applications/admin/languages/es.py index 99b23604..53eb9368 100644 --- a/applications/admin/languages/es.py +++ b/applications/admin/languages/es.py @@ -3,154 +3,28 @@ '!langcode!': 'es', '!langname!': 'Español', '"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN': '"actualice" es una expresión opcional como "campo1=\'nuevo_valor\'". No se puede actualizar o eliminar resultados de un JOIN', -'%Y-%m-%d': '%Y-%m-%d', -'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', '%s %%{row} deleted': '%s filas eliminadas', '%s %%{row} updated': '%s filas actualizadas', +'%Y-%m-%d': '%Y-%m-%d', +'%Y-%m-%d %H:%M:%S': '%Y-%m-%d %H:%M:%S', '(something like "it-it")': '(algo como "it-it")', +'@markmin\x01(file **gluon/contrib/plural_rules/%s.py** is not found)': '(file **gluon/contrib/plural_rules/%s.py** is not found)', '@markmin\x01Searching: **%s** %%{file}': 'Searching: **%s** files', 'A new version of web2py is available': 'Hay una nueva versión de web2py disponible', 'A new version of web2py is available: %s': 'Hay una nueva versión de web2py disponible: %s', -'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCION: Inicio de sesión requiere una conexión segura (HTTPS) o localhost.', -'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', -'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que se ejecuta!', 'About': 'acerca de', 'About application': 'Acerca de la aplicación', -'Admin is disabled because insecure channel': 'Admin deshabilitado, el canal no es seguro', -'Admin is disabled because unsecure channel': 'Admin deshabilitado, el canal no es seguro', -'Administrator Password:': 'Contraseña del Administrador:', -'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?', -'Are you sure you want to delete plugin "%s"?': '¿Está seguro que quiere eliminar el plugin "%s"?', -'Are you sure you want to uninstall application "%s"': '¿Está seguro que desea desinstalar la aplicación "%s"', -'Are you sure you want to uninstall application "%s"?': '¿Está seguro que desea desinstalar la aplicación "%s"?', -'Are you sure you want to upgrade web2py now?': '¿Está seguro que desea actualizar web2py ahora?', -'Available databases and tables': 'Bases de datos y tablas disponibles', -'Cannot be empty': 'No puede estar vacío', -'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.', -'Cannot compile: there are errors in your app:': 'No se puede compilar: hay errores en su aplicación:', -'Change Password': 'Cambie Contraseña', -'Change admin password': 'cambie contraseña admin', -'Check to delete': 'Marque para eliminar', -'Checking for upgrades...': 'Buscando actulizaciones...', -'Clean': 'limpiar', -'Click row to expand traceback': 'Click row to expand traceback', -'Client IP': 'IP del Cliente', -'Compile': 'compilar', -'Controllers': 'Controladores', -'Count': 'Count', -'Create': 'crear', -'Create new application using the Wizard': 'Create new application using the Wizard', -'Create new simple application': 'Cree una nueva aplicación', -'Current request': 'Solicitud en curso', -'Current response': 'Respuesta en curso', -'Current session': 'Sesión en curso', -'DESIGN': 'DISEÑO', -'Date and Time': 'Fecha y Hora', -'Delete': 'Elimine', -'Delete:': 'Elimine:', -'Deploy on Google App Engine': 'Instale en Google App Engine', -'Description': 'Descripción', -'Design for': 'Diseño para', -'E-mail': 'Correo electrónico', -'EDIT': 'EDITAR', -'Edit': 'editar', -'Edit Profile': 'Editar Perfil', -'Edit application': 'Editar aplicación', -'Edit current record': 'Edite el registro actual', -'Editing Language file': 'Editando archivo de lenguaje', -'Editing file': 'Editando archivo', -'Editing file "%s"': 'Editando archivo "%s"', -'Enterprise Web Framework': 'Armazón Empresarial para Internet', -'Error': 'Error', -'Error logs for "%(app)s"': 'Bitácora de errores en "%(app)s"', -'Errors': 'errores', -'Exception instance attributes': 'Atributos de la instancia de Excepción', -'File': 'File', -'First name': 'Nombre', -'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].', -'Group ID': 'ID de Grupo', -'Hello World': 'Hola Mundo', -'Help': 'ayuda', -'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'Si el reporte anterior contiene un número de tiquete este indica un falla en la ejecución del controlador, antes de cualquier intento de ejecutat doctests. Esto generalmente se debe a un error en la indentación o un error por fuera del código de la función.\r\nUn titulo verde indica que todas las pruebas pasaron (si existen). En dicho caso los resultados no se muestran.', -'Import/Export': 'Importar/Exportar', -'Install': 'instalar', -'Installed applications': 'Aplicaciones instaladas', -'Internal State': 'Estado Interno', -'Invalid Query': 'Consulta inválida', -'Invalid action': 'Acción inválida', -'Invalid email': 'Correo inválido', -'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados', -'Languages': 'Lenguajes', -'Last name': 'Apellido', -'Last saved on:': 'Guardado en:', -'License for': 'Licencia para', -'Login': 'Inicio de sesión', -'Login to the Administrative Interface': 'Inicio de sesión para la Interfaz Administrativa', -'Logout': 'fin de sesión', -'Lost Password': 'Contraseña perdida', -'Models': 'Modelos', -'Modules': 'Módulos', -'NO': 'NO', -'Name': 'Nombre', -'New Record': 'Registro nuevo', -'No databases in this application': 'No hay bases de datos en esta aplicación', -'Origin': 'Origen', -'Original/Translation': 'Original/Traducción', -'Overwrite installed app': 'sobreescriba aplicación instalada', -'PAM authenticated user, cannot change password here': 'usuario autenticado por PAM, no puede cambiar la contraseña aquí', -'Pack all': 'empaquetar todo', -'Pack compiled': 'empaquete compiladas', -'Password': 'Contraseña', -'Peeking at file': 'Visualizando archivo', -'Plugin "%s" in application': 'Plugin "%s" en aplicación', -'Plugins': 'Plugins', -'Powered by': 'Este sitio usa', -'Query:': 'Consulta:', -'Record ID': 'ID de Registro', -'Register': 'Registrese', -'Registration key': 'Contraseña de Registro', -'Remove compiled': 'eliminar compiladas', -'Resolve Conflict file': 'archivo Resolución de Conflicto', -'Role': 'Rol', -'Rows in table': 'Filas en la tabla', -'Rows selected': 'Filas seleccionadas', -'Saved file hash:': 'Hash del archivo guardado:', -'Site': 'sitio', -'Static files': 'Archivos estáticos', -'Sure you want to delete this object?': '¿Está seguro que desea eliminar este objeto?', -'TM': 'MR', -'Table name': 'Nombre de la tabla', -'Testing application': 'Probando aplicación', -'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "consulta" es una condición como "db.tabla1.campo1==\'valor\'". Algo como "db.tabla1.campo1==db.tabla2.campo2" resulta en un JOIN SQL.', -'There are no controllers': 'No hay controladores', -'There are no models': 'No hay modelos', -'There are no modules': 'No hay módulos', -'There are no static files': 'No hay archivos estáticos', -'There are no translators, only default language is supported': 'No hay traductores, sólo el lenguaje por defecto es soportado', -'There are no views': 'No hay vistas', -'This is the %(filename)s template': 'Esta es la plantilla %(filename)s', -'Ticket': 'Tiquete', -'Timestamp': 'Timestamp', -'To create a plugin, name a file/folder plugin_[name]': 'Para crear un plugin, nombre un archivo/carpeta plugin_[nombre]', -'Unable to check for upgrades': 'No es posible verificar la existencia de actualizaciones', -'Unable to download': 'No es posible la descarga', -'Unable to download app': 'No es posible descargar la aplicación', -'Unable to download app because:': 'No es posible descargar la aplicación porque:', -'Unable to download because': 'No es posible descargar porque', -'Uninstall': 'desinstalar', -'Update:': 'Actualice:', -'Upload & install packed application': 'Suba e instale aplicación empaquetada', -'Upload existing application': 'Suba esta aplicación', -'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, y ~(...) para NOT, para crear consultas más complejas.', -'User ID': 'ID de Usuario', -'Version': 'Versión', -'Views': 'Vistas', -'Welcome to web2py': 'Bienvenido a web2py', -'YES': 'SI', 'additional code for your application': 'código adicional para su aplicación', +'Additional code for your application': 'Additional code for your application', 'admin disabled because no admin password': ' por falta de contraseña', 'admin disabled because not supported on google app engine': 'admin deshabilitado, no es soportado en GAE', 'admin disabled because unable to access password file': 'admin deshabilitado, imposible acceder al archivo con la contraseña', +'Admin is disabled because insecure channel': 'Admin deshabilitado, el canal no es seguro', +'Admin is disabled because unsecure channel': 'Admin deshabilitado, el canal no es seguro', +'Admin language': 'Admin language', +'administrative interface': 'administrative interface', +'Administrator Password:': 'Contraseña del Administrador:', +'An error occured, please %s the page': 'An error occured, please %s the page', 'and rename it (required):': 'y renombrela (requerido):', 'and rename it:': ' y renombrelo:', 'appadmin': 'appadmin', @@ -158,47 +32,115 @@ 'application "%s" uninstalled': 'aplicación "%s" desinstalada', 'application compiled': 'aplicación compilada', 'application is compiled and cannot be designed': 'la aplicación está compilada y no puede ser modificada', +'are not used': 'are not used', +'are not used yet': 'are not used yet', +'Are you sure you want to delete file "%s"?': '¿Está seguro que desea eliminar el archivo "%s"?', +'Are you sure you want to delete plugin "%s"?': '¿Está seguro que quiere eliminar el plugin "%s"?', +'Are you sure you want to delete this object?': 'Are you sure you want to delete this object?', +'Are you sure you want to uninstall application "%s"': '¿Está seguro que desea desinstalar la aplicación "%s"', +'Are you sure you want to uninstall application "%s"?': '¿Está seguro que desea desinstalar la aplicación "%s"?', +'Are you sure you want to upgrade web2py now?': '¿Está seguro que desea actualizar web2py ahora?', 'arguments': 'argumentos', +'at char %s': 'at char %s', +'at line %s': 'at line %s', +'ATTENTION: Login requires a secure (HTTPS) connection or running on localhost.': 'ATENCION: Inicio de sesión requiere una conexión segura (HTTPS) o localhost.', +'ATTENTION: TESTING IS NOT THREAD SAFE SO DO NOT PERFORM MULTIPLE TESTS CONCURRENTLY.': 'ATENCION: NO EJECUTE VARIAS PRUEBAS SIMULTANEAMENTE, NO SON THREAD SAFE.', +'ATTENTION: you cannot edit the running application!': 'ATENCION: no puede modificar la aplicación que se ejecuta!', +'Autocomplete': 'Autocomplete', +'Available databases and tables': 'Bases de datos y tablas disponibles', 'back': 'atrás', +'breakpoint': 'breakpoint', +'breakpoints': 'breakpoints', 'browse': 'buscar', 'cache': 'cache', 'cache, errors and sessions cleaned': 'cache, errores y sesiones eliminados', +'Cannot be empty': 'No puede estar vacío', +'Cannot compile: there are errors in your app. Debug it, correct errors and try again.': 'No se puede compilar: hay errores en su aplicación. Depure, corrija errores y vuelva a intentarlo.', +'Cannot compile: there are errors in your app:': 'No se puede compilar: hay errores en su aplicación:', 'cannot create file': 'no es posible crear archivo', 'cannot upload file "%(filename)s"': 'no es posible subir archivo "%(filename)s"', +'Change admin password': 'cambie contraseña admin', +'Change Password': 'Cambie Contraseña', 'check all': 'marcar todos', +'Check to delete': 'Marque para eliminar', +'Checking for upgrades...': 'Buscando actulizaciones...', +'Clean': 'limpiar', 'click here for online examples': 'haga clic aquí para ver ejemplos en línea', 'click here for the administrative interface': 'haga clic aquí para usar la interfaz administrativa', +'Click row to expand traceback': 'Click row to expand traceback', 'click to check for upgrades': 'haga clic para buscar actualizaciones', 'click to open': 'click to open', +'Client IP': 'IP del Cliente', 'code': 'código', +'Code listing': 'Code listing', +'collapse/expand all': 'collapse/expand all', 'commit (mercurial)': 'commit (mercurial)', +'Compile': 'compilar', 'compiled application removed': 'aplicación compilada removida', +'continue': 'continue', +'Controllers': 'Controladores', 'controllers': 'controladores', +'Count': 'Count', +'Create': 'crear', 'create file with filename:': 'cree archivo con nombre:', +'Create new application using the Wizard': 'Create new application using the Wizard', 'create new application:': 'nombre de la nueva aplicación:', +'Create new simple application': 'Cree una nueva aplicación', 'created by': 'creado por', 'crontab': 'crontab', +'Current request': 'Solicitud en curso', +'Current response': 'Respuesta en curso', +'Current session': 'Sesión en curso', 'currently saved or': 'actualmente guardado o', 'customize me!': 'Adaptame!', 'data uploaded': 'datos subidos', 'database': 'base de datos', 'database %s select': 'selección en base de datos %s', 'database administration': 'administración base de datos', +'Date and Time': 'Fecha y Hora', 'db': 'db', +'Debug': 'Debug', 'defines tables': 'define tablas', +'Delete': 'Elimine', 'delete': 'eliminar', 'delete all checked': 'eliminar marcados', 'delete plugin': 'eliminar plugin', +'Delete this file (you will be asked to confirm deletion)': 'Delete this file (you will be asked to confirm deletion)', +'Delete:': 'Elimine:', +'Deploy on Google App Engine': 'Instale en Google App Engine', +'Description': 'Descripción', 'design': 'modificar', +'DESIGN': 'DISEÑO', +'Design for': 'Diseño para', 'direction: ltr': 'direction: ltr', +'docs': 'docs', 'done!': 'listo!', +'download layouts': 'download layouts', +'download plugins': 'download plugins', +'E-mail': 'Correo electrónico', +'EDIT': 'EDITAR', +'Edit': 'editar', +'Edit application': 'Editar aplicación', 'edit controller': 'editar controlador', +'Edit current record': 'Edite el registro actual', +'Edit Profile': 'Editar Perfil', 'edit views:': 'editar vistas:', +'Editing file': 'Editando archivo', +'Editing file "%s"': 'Editando archivo "%s"', +'Editing Language file': 'Editando archivo de lenguaje', +'Enterprise Web Framework': 'Armazón Empresarial para Internet', +'Error': 'Error', +'Error logs for "%(app)s"': 'Bitácora de errores en "%(app)s"', +'Errors': 'errores', +'Exception instance attributes': 'Atributos de la instancia de Excepción', 'export as csv file': 'exportar como archivo CSV', 'exposes': 'expone', +'exposes:': 'exposes:', 'extends': 'extiende', +'failed to compile file because:': 'failed to compile file because:', 'failed to reload module': 'recarga del módulo ha fallado', 'failed to reload module because:': 'no es posible recargar el módulo por:', +'File': 'File', 'file "%(filename)s" created': 'archivo "%(filename)s" creado', 'file "%(filename)s" deleted': 'archivo "%(filename)s" eliminado', 'file "%(filename)s" uploaded': 'archivo "%(filename)s" subido', @@ -208,76 +150,211 @@ 'file does not exist': 'archivo no existe', 'file saved on %(time)s': 'archivo guardado %(time)s', 'file saved on %s': 'archivo guardado %s', +'filter': 'filter', +'Find Next': 'Find Next', +'Find Previous': 'Find Previous', +'First name': 'Nombre', +'Functions with no doctests will result in [passed] tests.': 'Funciones sin doctests equivalen a pruebas [aceptadas].', +'Globals##debug': 'Globals', +'graph model': 'graph model', +'Group ID': 'ID de Grupo', +'Hello World': 'Hola Mundo', +'Help': 'ayuda', 'htmledit': 'htmledit', +'If the report above contains a ticket number it indicates a failure in executing the controller, before any attempt to execute the doctests. This is usually due to an indentation error or an error outside function code.\nA green title indicates that all tests (if defined) passed. In this case test results are not shown.': 'Si el reporte anterior contiene un número de tiquete este indica un falla en la ejecución del controlador, antes de cualquier intento de ejecutat doctests. Esto generalmente se debe a un error en la indentación o un error por fuera del código de la función.\r\nUn titulo verde indica que todas las pruebas pasaron (si existen). En dicho caso los resultados no se muestran.', +'Import/Export': 'Importar/Exportar', 'includes': 'incluye', 'insert new': 'inserte nuevo', 'insert new %s': 'inserte nuevo %s', +'Install': 'instalar', +'Installed applications': 'Aplicaciones instaladas', +'Interaction at %s line %s': 'Interaction at %s line %s', +'Interactive console': 'Interactive console', 'internal error': 'error interno', +'Internal State': 'Estado Interno', +'Invalid action': 'Acción inválida', +'Invalid email': 'Correo inválido', 'invalid password': 'contraseña inválida', +'Invalid Query': 'Consulta inválida', 'invalid request': 'solicitud inválida', 'invalid ticket': 'tiquete inválido', +'Key bindings': 'Key bindings', 'language file "%(filename)s" created/updated': 'archivo de lenguaje "%(filename)s" creado/actualizado', +'Language files (static strings) updated': 'Archivos de lenguaje (cadenas estáticas) actualizados', 'languages': 'lenguajes', +'Languages': 'Lenguajes', 'languages updated': 'lenguajes actualizados', +'Last name': 'Apellido', +'Last saved on:': 'Guardado en:', +'License for': 'Licencia para', 'loading...': 'cargando...', +'Locals##debug': 'Locals', +'Login': 'Inicio de sesión', 'login': 'inicio de sesión', +'Login to the Administrative Interface': 'Inicio de sesión para la Interfaz Administrativa', +'Logout': 'fin de sesión', +'Lost Password': 'Contraseña perdida', 'manage': 'manage', 'merge': 'combinar', +'Models': 'Modelos', 'models': 'modelos', +'Modules': 'Módulos', 'modules': 'módulos', +'Name': 'Nombre', 'new application "%s" created': 'nueva aplicación "%s" creada', 'new plugin installed': 'nuevo plugin instalado', +'New Record': 'Registro nuevo', 'new record inserted': 'nuevo registro insertado', +'next': 'next', 'next 100 rows': '100 filas siguientes', +'NO': 'NO', +'No databases in this application': 'No hay bases de datos en esta aplicación', +'No Interaction yet': 'No Interaction yet', 'no match': 'no encontrado', +'or alternatively': 'or alternatively', 'or import from csv file': 'o importar desde archivo CSV', 'or provide app url:': 'o provea URL de la aplicación:', 'or provide application url:': 'o provea URL de la aplicación:', +'Origin': 'Origen', +'Original/Translation': 'Original/Traducción', +'Overwrite installed app': 'sobreescriba aplicación instalada', +'Pack all': 'empaquetar todo', +'Pack compiled': 'empaquete compiladas', 'pack plugin': 'empaquetar plugin', +'PAM authenticated user, cannot change password here': 'usuario autenticado por PAM, no puede cambiar la contraseña aquí', +'Password': 'Contraseña', 'password changed': 'contraseña cambiada', +'Peeking at file': 'Visualizando archivo', +'Please': 'Please', 'plugin "%(plugin)s" deleted': 'plugin "%(plugin)s" eliminado', +'Plugin "%s" in application': 'Plugin "%s" en aplicación', +'plugins': 'plugins', +'Plugins': 'Plugins', +'Plural-Forms:': 'Plural-Forms:', +'Powered by': 'Este sitio usa', 'previous 100 rows': '100 filas anteriores', +'Private files': 'Private files', +'private files': 'private files', +'Query:': 'Consulta:', 'record': 'registro', 'record does not exist': 'el registro no existe', 'record id': 'id de registro', +'Record ID': 'ID de Registro', +'refresh': 'refresh', +'Register': 'Registrese', +'Registration key': 'Contraseña de Registro', +'reload': 'reload', +'Remove compiled': 'eliminar compiladas', +'Removed Breakpoint on %s at line %s': 'Removed Breakpoint on %s at line %s', +'Replace': 'Replace', +'Replace All': 'Replace All', +'Resolve Conflict file': 'archivo Resolución de Conflicto', 'restore': 'restaurar', +'return': 'return', 'revert': 'revertir', +'Role': 'Rol', +'Rows in table': 'Filas en la tabla', +'Rows selected': 'Filas seleccionadas', +'rules are not defined': 'rules are not defined', +"Run tests in this file (to run all files, you may also use the button labelled 'test')": "Run tests in this file (to run all files, you may also use the button labelled 'test')", +'Save': 'Save', 'save': 'guardar', +'Save file:': 'Save file:', +'Save via Ajax': 'Save via Ajax', +'Saved file hash:': 'Hash del archivo guardado:', 'selected': 'seleccionado(s)', 'session expired': 'sesión expirada', +'Set Breakpoint on %s at line %s: %s': 'Set Breakpoint on %s at line %s: %s', 'shell': 'shell', +'Site': 'sitio', 'some files could not be removed': 'algunos archivos no pudieron ser removidos', +'Start searching': 'Start searching', 'state': 'estado', 'static': 'estáticos', +'Static': 'Static', +'Static files': 'Archivos estáticos', +'step': 'step', +'stop': 'stop', 'submit': 'enviar', +'successful': 'successful', +'Sure you want to delete this object?': '¿Está seguro que desea eliminar este objeto?', 'table': 'tabla', +'Table name': 'Nombre de la tabla', 'test': 'probar', +'Testing application': 'Probando aplicación', +'The "query" is a condition like "db.table1.field1==\'value\'". Something like "db.table1.field1==db.table2.field2" results in a SQL JOIN.': 'La "consulta" es una condición como "db.tabla1.campo1==\'valor\'". Algo como "db.tabla1.campo1==db.tabla2.campo2" resulta en un JOIN SQL.', 'the application logic, each URL path is mapped in one exposed function in the controller': 'la lógica de la aplicación, cada ruta URL se mapea en una función expuesta en el controlador', +'The application logic, each URL path is mapped in one exposed function in the controller': 'The application logic, each URL path is mapped in one exposed function in the controller', 'the data representation, define database tables and sets': 'la representación de datos, define tablas y conjuntos de base de datos', +'The data representation, define database tables and sets': 'The data representation, define database tables and sets', +'The presentations layer, views are also known as templates': 'The presentations layer, views are also known as templates', 'the presentations layer, views are also known as templates': 'la capa de presentación, las vistas también son llamadas plantillas', +'There are no controllers': 'No hay controladores', +'There are no models': 'No hay modelos', +'There are no modules': 'No hay módulos', +'There are no plugins': 'There are no plugins', +'There are no private files': 'There are no private files', +'There are no static files': 'No hay archivos estáticos', +'There are no translators, only default language is supported': 'No hay traductores, sólo el lenguaje por defecto es soportado', +'There are no views': 'No hay vistas', +'These files are not served, they are only available from within your app': 'These files are not served, they are only available from within your app', +'These files are served without processing, your images go here': 'These files are served without processing, your images go here', 'these files are served without processing, your images go here': 'estos archivos son servidos sin procesar, sus imágenes van aquí', +'This is the %(filename)s template': 'Esta es la plantilla %(filename)s', +'this page to see if a breakpoint was hit and debug interaction is required.': 'this page to see if a breakpoint was hit and debug interaction is required.', +'Ticket': 'Tiquete', +'Timestamp': 'Timestamp', +'TM': 'MR', 'to previous version.': 'a la versión previa.', +'To create a plugin, name a file/folder plugin_[name]': 'Para crear un plugin, nombre un archivo/carpeta plugin_[nombre]', +'To emulate a breakpoint programatically, write:': 'To emulate a breakpoint programatically, write:', +'to use the debugger!': 'to use the debugger!', +'toggle breakpoint': 'toggle breakpoint', +'Toggle Fullscreen': 'Toggle Fullscreen', 'translation strings for the application': 'cadenas de caracteres de traducción para la aplicación', +'Translation strings for the application': 'Translation strings for the application', 'try': 'intente', 'try something like': 'intente algo como', +'Type some Python code in here and hit Return (Enter) to execute it.': 'Type some Python code in here and hit Return (Enter) to execute it.', +'Unable to check for upgrades': 'No es posible verificar la existencia de actualizaciones', 'unable to create application "%s"': 'no es posible crear la aplicación "%s"', 'unable to delete file "%(filename)s"': 'no es posible eliminar el archivo "%(filename)s"', 'unable to delete file plugin "%(plugin)s"': 'no es posible eliminar plugin "%(plugin)s"', +'Unable to download': 'No es posible la descarga', +'Unable to download app': 'No es posible descargar la aplicación', +'Unable to download app because:': 'No es posible descargar la aplicación porque:', +'Unable to download because': 'No es posible descargar porque', 'unable to parse csv file': 'no es posible analizar el archivo CSV', 'unable to uninstall "%s"': 'no es posible instalar "%s"', 'unable to upgrade because "%s"': 'no es posible actualizar porque "%s"', 'uncheck all': 'desmarcar todos', +'Uninstall': 'desinstalar', 'update': 'actualizar', 'update all languages': 'actualizar todos los lenguajes', +'Update:': 'Actualice:', 'upgrade web2py now': 'actualize web2py ahora', +'Upload': 'Upload', +'Upload & install packed application': 'Suba e instale aplicación empaquetada', 'upload application:': 'subir aplicación:', +'Upload existing application': 'Suba esta aplicación', 'upload file:': 'suba archivo:', 'upload plugin file:': 'suba archivo de plugin:', +'Use (...)&(...) for AND, (...)|(...) for OR, and ~(...) for NOT to build more complex queries.': 'Use (...)&(...) para AND, (...)|(...) para OR, y ~(...) para NOT, para crear consultas más complejas.', +'User ID': 'ID de Usuario', 'variables': 'variables', +'Version': 'Versión', 'versioning': 'versiones', +'Versioning': 'Versioning', 'view': 'vista', +'Views': 'Vistas', 'views': 'vistas', -'web2py Recent Tweets': 'Tweets Recientes de web2py', 'web2py is up to date': 'web2py está actualizado', +'web2py online debugger': 'web2py online debugger', +'web2py Recent Tweets': 'Tweets Recientes de web2py', 'web2py upgraded; please restart it': 'web2py actualizado; favor reiniciar', +'Welcome to web2py': 'Bienvenido a web2py', +'YES': 'SI', +'You need to set up and reach a': 'You need to set up and reach a', +'Your application will be blocked until you click an action button (next, step, continue, etc.)': 'Your application will be blocked until you click an action button (next, step, continue, etc.)', +'Your can inspect variables using the console below': 'Your can inspect variables using the console below', } diff --git a/applications/admin/static/codemirror/README.md b/applications/admin/static/codemirror/README.md index 8ed9871a..3e87272f 100644 --- a/applications/admin/static/codemirror/README.md +++ b/applications/admin/static/codemirror/README.md @@ -4,5 +4,6 @@ CodeMirror is a JavaScript component that provides a code editor in the browser. When a mode is available for the language you are coding in, it will color your code, and optionally help with indentation. -The project page is http://codemirror.net -The manual is at http://codemirror.net/doc/manual.html +The project page is http://codemirror.net +The manual is at http://codemirror.net/doc/manual.html +The contributing guidelines are in the CONTRIBUTING.md file diff --git a/applications/admin/static/codemirror/emmet.min.js b/applications/admin/static/codemirror/emmet.min.js index 9c84ddcb..96de30b9 100644 --- a/applications/admin/static/codemirror/emmet.min.js +++ b/applications/admin/static/codemirror/emmet.min.js @@ -1,284 +1,304 @@ -/* from http://code.google.com/p/zen-coding/ MIT license */ -var _=function(){function h(a,b,d){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual&&k.isFunction(a.isEqual))return a.isEqual(b);if(b.isEqual&&k.isFunction(b.isEqual))return b.isEqual(a);var c=i.call(a);if(c!=i.call(b))return!1;switch(c){case "[object String]":return a==String(b);case "[object Number]":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case "[object Date]":case "[object Boolean]":return+a==+b;case "[object RegExp]":return a.source== -b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return!1;for(var e=d.length;e--;)if(d[e]==a)return!0;d.push(a);var e=0,f=!0;if(c=="[object Array]"){if(e=a.length,f=e==b.length)for(;e--;)if(!(f=e in a==e in b&&h(a[e],b[e],d)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var j in a)if(k.has(a,j)&&(e++,!(f=k.has(b,j)&&h(a[j],b[j],d))))break;if(f){for(j in b)if(k.has(b,j)&&!e--)break; -f=!e}}d.pop();return f}var e=this,g=e._,f={},c=Array.prototype,b=Object.prototype,a=c.slice,d=c.unshift,i=b.toString,j=b.hasOwnProperty,l=c.forEach,n=c.map,m=c.reduce,o=c.reduceRight,q=c.filter,r=c.every,t=c.some,x=c.indexOf,u=c.lastIndexOf,b=Array.isArray,z=Object.keys,A=Function.prototype.bind,k=function(a){return new B(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=k;exports._=k}else e._=k;k.VERSION="1.3.3";var v=k.each=k.forEach=function(a, -b,d){if(a!=null)if(l&&a.forEach===l)a.forEach(b,d);else if(a.length===+a.length)for(var c=0,e=a.length;c2;a==null&&(a=[]);if(m&& -a.reduce===m)return c&&(b=k.bind(b,c)),e?a.reduce(b,d):a.reduce(b);v(a,function(a,i,f){e?d=b.call(c,d,a,i,f):(d=a,e=!0)});if(!e)throw new TypeError("Reduce of empty array with no initial value");return d};k.reduceRight=k.foldr=function(a,b,d,c){var e=arguments.length>2;a==null&&(a=[]);if(o&&a.reduceRight===o)return c&&(b=k.bind(b,c)),e?a.reduceRight(b,d):a.reduceRight(b);var i=k.toArray(a).reverse();c&&!e&&(b=k.bind(b,c));return e?k.reduce(i,b,d,c):k.reduce(i,b)};k.find=k.detect=function(a,b,d){var c; -F(a,function(a,e,i){if(b.call(d,a,e,i))return c=a,!0});return c};k.filter=k.select=function(a,b,d){var c=[];if(a==null)return c;if(q&&a.filter===q)return a.filter(b,d);v(a,function(a,e,i){b.call(d,a,e,i)&&(c[c.length]=a)});return c};k.reject=function(a,b,c){var d=[];if(a==null)return d;v(a,function(a,e,i){b.call(c,a,e,i)||(d[d.length]=a)});return d};k.every=k.all=function(a,b,d){var c=!0;if(a==null)return c;if(r&&a.every===r)return a.every(b,d);v(a,function(a,e,i){if(!(c=c&&b.call(d,a,e,i)))return f}); -return!!c};var F=k.some=k.any=function(a,b,c){b||(b=k.identity);var d=!1;if(a==null)return d;if(t&&a.some===t)return a.some(b,c);v(a,function(a,e,i){if(d||(d=b.call(c,a,e,i)))return f});return!!d};k.include=k.contains=function(a,b){var d=!1;return a==null?d:x&&a.indexOf===x?a.indexOf(b)!=-1:d=F(a,function(a){return a===b})};k.invoke=function(b,d){var c=a.call(arguments,2);return k.map(b,function(a){return(k.isFunction(d)?d||a:a[d]).apply(a,c)})};k.pluck=function(a,b){return k.map(a,function(a){return a[b]})}; -k.max=function(a,b,d){if(!b&&k.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!b&&k.isEmpty(a))return-Infinity;var c={computed:-Infinity};v(a,function(a,e,i){e=b?b.call(d,a,e,i):a;e>=c.computed&&(c={value:a,computed:e})});return c.value};k.min=function(a,b,d){if(!b&&k.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!b&&k.isEmpty(a))return Infinity;var c={computed:Infinity};v(a,function(a,e,i){e=b?b.call(d,a,e,i):a;ec?1:0}),"value")};k.groupBy=function(a,b){var d={},c=k.isFunction(b)?b:function(a){return a[b]};v(a,function(a,b){var e=c(a,b);(d[e]||(d[e]=[])).push(a)}); -return d};k.sortedIndex=function(a,b,d){d||(d=k.identity);for(var c=0,e=a.length;c>1;d(a[i])=0})})};k.difference=function(b){var d=k.flatten(a.call(arguments,1),!0);return k.filter(b,function(a){return!k.include(d,a)})};k.zip=function(){for(var b=a.call(arguments),d=k.max(k.pluck(b,"length")), -c=Array(d),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};k.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};k.keys=z||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)k.has(a,d)&&(b[b.length]=d);return b};k.values=function(a){return k.map(a,k.identity)};k.functions=k.methods=function(a){var b=[],d;for(d in a)k.isFunction(a[d])&&b.push(d);return b.sort()};k.extend=function(b){v(a.call(arguments, -1),function(a){for(var d in a)b[d]=a[d]});return b};k.pick=function(b){var d={};v(k.flatten(a.call(arguments,1)),function(a){a in b&&(d[a]=b[a])});return d};k.defaults=function(b){v(a.call(arguments,1),function(a){for(var d in a)b[d]==null&&(b[d]=a[d])});return b};k.clone=function(a){return!k.isObject(a)?a:k.isArray(a)?a.slice():k.extend({},a)};k.tap=function(a,b){b(a);return a};k.isEqual=function(a,b){return h(a,b,[])};k.isEmpty=function(a){if(a==null)return!0;if(k.isArray(a)||k.isString(a))return a.length=== -0;for(var b in a)if(k.has(a,b))return!1;return!0};k.isElement=function(a){return!!(a&&a.nodeType==1)};k.isArray=b||function(a){return i.call(a)=="[object Array]"};k.isObject=function(a){return a===Object(a)};k.isArguments=function(a){return i.call(a)=="[object Arguments]"};if(!k.isArguments(arguments))k.isArguments=function(a){return!(!a||!k.has(a,"callee"))};k.isFunction=function(a){return i.call(a)=="[object Function]"};k.isString=function(a){return i.call(a)=="[object String]"};k.isNumber=function(a){return i.call(a)== -"[object Number]"};k.isFinite=function(a){return k.isNumber(a)&&isFinite(a)};k.isNaN=function(a){return a!==a};k.isBoolean=function(a){return a===!0||a===!1||i.call(a)=="[object Boolean]"};k.isDate=function(a){return i.call(a)=="[object Date]"};k.isRegExp=function(a){return i.call(a)=="[object RegExp]"};k.isNull=function(a){return a===null};k.isUndefined=function(a){return a===void 0};k.has=function(a,b){return j.call(a,b)};k.noConflict=function(){e._=g;return this};k.identity=function(a){return a}; -k.times=function(a,b,d){for(var c=0;c/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};k.result=function(a,b){if(a==null)return null;var d=a[b];return k.isFunction(d)?d.call(a):d};k.mixin=function(a){v(k.functions(a),function(b){H(b,k[b]=a[b])})};var s=0;k.uniqueId=function(a){var b=s++;return a?a+b:b};k.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g, -escape:/<%-([\s\S]+?)%>/g};var w=/.^/,y={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},C;for(C in y)y[y[C]]=C;var I=/\\|'|\r|\n|\t|\u2028|\u2029/g,J=/\\(\\|'|r|n|t|u2028|u2029)/g,D=function(a){return a.replace(J,function(a,b){return y[b]})};k.template=function(a,b,d){d=k.defaults(d||{},k.templateSettings);a="__p+='"+a.replace(I,function(a){return"\\"+y[a]}).replace(d.escape||w,function(a,b){return"'+\n_.escape("+D(b)+")+\n'"}).replace(d.interpolate||w,function(a,b){return"'+\n("+ -D(b)+")+\n'"}).replace(d.evaluate||w,function(a,b){return"';\n"+D(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",c=new Function(d.variable||"obj","_",a);if(b)return c(b,k);b=function(a){return c.call(this,a,k)};b.source="function("+(d.variable||"obj")+"){\n"+a+"}";return b};k.chain=function(a){return k(a).chain()};var B=function(a){this._wrapped=a};k.prototype=B.prototype; -var E=function(a,b){return b?k(a).chain():a},H=function(b,c){B.prototype[b]=function(){var b=a.call(arguments);d.call(b,this._wrapped);return E(c.apply(k,b),this._chain)}};k.mixin(k);v(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=c[a];B.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var c=d.length;(a=="shift"||a=="splice")&&c===0&&delete d[0];return E(d,this._chain)}});v(["concat","join","slice"],function(a){var b=c[a];B.prototype[a]=function(){return E(b.apply(this._wrapped, -arguments),this._chain)}});B.prototype.chain=function(){this._chain=!0;return this};B.prototype.value=function(){return this._wrapped};return k}.call({}),emmet=function(h,e){function g(a,b,i){var f;f=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};e.extend(f,a);c.prototype=a.prototype;f.prototype=new c;b&&e.extend(f.prototype,b);i&&e.extend(f,i);f.prototype.constructor=f;f.__super__=a.prototype;return f}var f={_:e},c=function(){},b=null;return{define:function(a, -b){a in f||(f[a]=e.isFunction(b)?this.exec(b):b)},require:function(a){!(a in f)&&b&&b(a);return f[a]},exec:function(a,b){return a.call(b||h,e.bind(this.require,this),e,this)},extend:function(a,b){var c=g(this,a,b);c.extend=this.extend;if(a.hasOwnProperty("toString"))c.prototype.toString=a.toString;return c},expandAbbreviation:function(a,b,c,e){if(!a)return"";var b=b||"html",c=c||"plain",f=this.require("filters"),g=this.require("utils"),h=this.require("abbreviationParser"),c=this.require("profile").get(c, -b);this.require("tabStops").resetTabstopIndex();a=f.extractFromAbbreviation(a);e=h.parse(a[0],{syntax:b,contextNode:e});b=f.composeList(b,c,a[1]);f.apply(e,b,c);return g.replaceVariables(e.toString())},defaultSyntax:function(){return"html"},defaultProfile:function(){return"plain"},log:function(){h.console&&h.console.log&&h.console.log.apply(h.console,arguments)},setModuleLoader:function(a){b=a}}}(this,_); -emmet.define("abbreviationParser",function(h,e){function g(){this.parent=null;this.children=[];this._attributes=[];this.abbreviation="";this.counter=1;this._name=null;this._text="";this.repeatCount=1;this.hasImplicitRepeat=!1;this._data={};this.padding=this.content=this.end=this.start=""}function f(a){return a.substring(1,a.length-1)}function c(a){for(var a=h("utils").trim(a),b=new g,d=b.addChild(),i,j=h("stringStream").create(a),a=1E3,m;!j.eol()&&--a>0;)switch(i=j.peek(),i){case "(":j.start=j.pos; -if(j.skipToPair("(",")"))i=c(f(j.current())),(m=j.match(/^\*(\d+)?/,!0))&&d._setRepeat(m[1]),e.each(i.children,function(a){d.addChild(a)});else throw'Invalid abbreviation: mo matching ")" found for character at '+j.pos;break;case ">":d=d.addChild();j.next();break;case "+":d=d.parent.addChild();j.next();break;case "^":i=d.parent||d;d=(i.parent||i).addChild();j.next();break;default:j.start=j.pos,j.eatWhile(function(a){if(a=="["||a=="{"){if(j.skipToPair(a,q[a]))return j.backUp(1),!0;throw'Invalid abbreviation: mo matching "'+ -q[a]+'" found for character at '+j.pos;}return a=="+"?(j.next(),a=j.eol()||~"+>^*".indexOf(j.peek()),j.backUp(1),a):a!="("&&n(a)}),d.setAbbreviation(j.current()),j.start=j.pos}if(a<1)throw"Endless loop detected";return b}function b(a){var a=h("utils").trim(a),b=[],a=h("stringStream").create(a);for(a.eatSpace();!a.eol();)if(a.start=a.pos,a.eatWhile(o)){var d=a.current(),c="";if(a.peek()=="="){a.next();a.start=a.pos;var e=a.peek();if(e=='"'||e=="'"){a.next();a:{for(var c=a,i=void 0;i=c.next();)if(i=== -e){c=!0;break a}c=!1}if(c)c=a.current(),c=c.substring(1,c.length-1);else throw"Invalid attribute value";}else if(a.eatWhile(/[^\s\]]/))c=a.current();else throw"Invalid attribute value";}b.push({name:d,value:c});a.eatSpace()}else break;return b}function a(a){for(var c=[],e={"#":"id",".":"class"},i=null,j=h("stringStream").create(a);!j.eol();)switch(j.peek()){case "#":case ".":if(i===null)i=j.pos;var g=e[j.peek()];j.next();j.start=j.pos;j.eatWhile(o);c.push({name:g,value:j.current()});break;case "[":if(i=== -null)i=j.pos;j.start=j.pos;if(!j.skipToPair("[","]"))throw"Invalid attribute set definition";c=c.concat(b(f(j.current())));break;default:j.next()}return!c.length?null:{element:a.substring(0,i),attributes:d(c)}}function d(a){var a=e.map(a,function(a){return e.clone(a)}),b={};return e.filter(a,function(a){if(!(a.name in b))return b[a.name]=a;var d=b[a.name];a.name.toLowerCase()=="class"?d.value+=(d.value.length?" ":"")+a.value:d.value=a.value;return!1})}function i(a){if(!~a.indexOf("{"))return null; -for(var b=h("stringStream").create(a);!b.eol();)switch(b.peek()){case "[":case "(":b.skipToPair(b.peek(),q[b.peek()]);break;case "{":return b.start=b.pos,b.skipToPair("{","}"),{element:a.substring(0,b.start),text:f(b.current())};default:b.next()}}function j(a){for(var b=a.children.length-1,d,c;b>=0;b--)if(c=a.children[b],c.isRepeating()){d=c.repeatCount;c.repeatCount=1;for(c.updateProperty("counter",1);--d>0;)c.parent.addChild(c.clone(),b+1).updateProperty("counter",d+1)}e.each(a.children,j);return a} -function l(a){for(var b=a.children.length-1;b>=0;b--){var d=a.children[b];d.isGroup()?d.replace(l(d).children):d.isEmpty()&&d.remove()}e.each(a.children,l);return a}function n(a){var b=a.charCodeAt(0);return b>64&&b<91||b>96&&b<123||b>47&&b<58||"#.*:$-_!@|".indexOf(a)!=-1}var m=/^[\w\-\$\:@\!]+\+?$/i,o=/[\w\-:\$]/,q={"[":"]","(":")","{":"}"},r=Array.prototype.splice,t=[],x=[],u=[];g.prototype={addChild:function(a,b){a=a||new g;a.parent=this;e.isUndefined(b)?this.children.push(a):this.children.splice(b, -0,a);return a},clone:function(){var a=new g;e.each(["abbreviation","counter","_name","_text","repeatCount","hasImplicitRepeat","start","end","content","padding"],function(b){a[b]=this[b]},this);a._attributes=e.map(this._attributes,function(a){return e.clone(a)});a._data=e.clone(this._data);a.children=e.map(this.children,function(b){b=b.clone();b.parent=a;return b});return a},remove:function(){if(this.parent)this.parent.children=e.without(this.parent.children,this);return this},replace:function(){var a= -this.parent,b=e.indexOf(a.children,this),d=e.flatten(arguments);r.apply(a.children,[b,1].concat(d));e.each(d,function(b){b.parent=a})},updateProperty:function(a,b){this[a]=b;e.each(this.children,function(d){d.updateProperty(a,b)})},find:function(a){return this.findAll(a)[0]},findAll:function(a){if(!e.isFunction(a))var b=a.toLowerCase(),a=function(a){return a.name().toLowerCase()==b};var d=[];e.each(this.children,function(b){a(b)&&d.push(b);d=d.concat(b.findAll(a))});return e.compact(d)},data:function(a, -b){if(arguments.length==2&&(this._data[a]=b,a=="resource"&&h("elements").is(b,"snippet")&&(this.content=b.data,this._text)))this.content=h("abbreviationUtils").insertChildContent(b.data,this._text);return this._data[a]},name:function(){var a=this.matchedResource();return h("elements").is(a,"element")?a.name:this._name},attributeList:function(){var a=[],b=this.matchedResource();h("elements").is(b,"element")&&e.isArray(b.attributes)&&(a=a.concat(b.attributes));return d(a.concat(this._attributes))}, -attribute:function(a,b){if(arguments.length==2){var d=e.indexOf(e.pluck(this._attributes,"name"),a.toLowerCase());if(~d)this._attributes[d].value=b}return(e.find(this.attributeList(),function(b){return b.name==a})||{}).value},matchedResource:function(){return this.data("resource")},index:function(){return this.parent?e.indexOf(this.parent.children,this):-1},_setRepeat:function(a){a?this.repeatCount=parseInt(a,10)||1:this.hasImplicitRepeat=!0},setAbbreviation:function(b){var d=this;this.abbreviation= -b=(b||"").replace(/\*(\d+)?$/,function(a,b){d._setRepeat(b);return""});var c=i(b);if(c)b=c.element,this.content=this._text=c.text;if(c=a(b))b=c.element,this._attributes=c.attributes;if((this._name=b)&&!m.test(this._name))throw"Invalid abbreviation";},toString:function(){var a=h("utils"),b=this.start,d=this.end,c=this.content,i=this;e.each(u,function(a){b=a(b,i,"start");c=a(c,i,"content");d=a(d,i,"end")});var j=e.map(this.children,function(a){return a.toString()}).join(""),c=h("abbreviationUtils").insertChildContent(c, -j,{keepVariable:!1});return b+a.padString(c,this.padding)+d},hasEmptyChildren:function(){return!!e.find(this.children,function(a){return a.isEmpty()})},hasImplicitName:function(){return!this._name&&!this.isTextNode()},isGroup:function(){return!this.abbreviation},isEmpty:function(){return!this.abbreviation&&!this.children.length},isRepeating:function(){return this.repeatCount>1||this.hasImplicitRepeat},isTextNode:function(){return!this.name()&&!this.attributeList().length},isElement:function(){return!this.isEmpty()&& -!this.isTextNode()},deepestChild:function(){if(!this.children.length)return null;for(var a=this;a.children.length;)a=e.last(a.children);return a}};u.push(function(a,b){return h("utils").replaceCounter(a,b.counter)});return{parse:function(a,b){var b=b||{},d=c(a);if(b.contextNode){d._name=b.contextNode.name;var i={};e.each(d._attributes,function(a){i[a.name]=a});e.each(b.contextNode.attributes,function(a){a.name in i?i[a.name].value=a.value:(a=e.clone(a),d._attributes.push(a),i[a.name]=a)})}e.each(t, -function(a){a(d,b)});d=l(j(d));e.each(x,function(a){a(d,b)});return d},AbbreviationNode:g,addPreprocessor:function(a){e.include(t,a)||t.push(a)},removeFilter:function(a){preprocessor=e.without(t,a)},addPostprocessor:function(a){e.include(x,a)||x.push(a)},removePostprocessor:function(a){x=e.without(x,a)},addOutputProcessor:function(a){e.include(u,a)||u.push(a)},removeOutputProcessor:function(a){u=e.without(u,a)},isAllowedChar:function(a){a=String(a);return n(a)||~">+^[](){}".indexOf(a)}}}); -emmet.exec(function(h,e){function g(f,c){var b=h("resources"),a=h("elements"),d=h("abbreviationParser");e.each(e.clone(f.children),function(i){var j=b.getMatchedResource(i,c);if(e.isString(j))i.data("resource",a.create("snippet",j));else if(a.is(j,"reference")){j=d.parse(j.data,{syntax:c});if(i.repeatCount>1){var f=j.findAll(function(a){return a.hasImplicitRepeat});e.each(f,function(a){a.repeatCount=i.repeatCount;a.hasImplicitRepeat=!1})}var h=j.deepestChild();h&&e.each(i.children,function(a){h.addChild(a)}); -i.replace(j.children)}else i.data("resource",j);g(i,c)})}h("abbreviationParser").addPreprocessor(function(e,c){var b=c.syntax||emmet.defaultSyntax();g(e,b)})}); -emmet.exec(function(h,e){function g(a){for(var b=h("range"),c=[],a=h("stringStream").create(a);!a.eol();){if(a.peek()=="\\")a.next();else if(a.start=a.pos,a.match(d,!0)){c.push(b.create(a.start,d));continue}a.next()}return c}function f(a,b){var d=h("utils"),c=g(a);c.reverse();e.each(c,function(c){a=d.replaceSubstring(a,b,c)});return a}function c(a){return g(a.content).length?!0:!!e.find(a.attributeList(),function(a){return!!g(a.value).length})}function b(a,b){var d=a.findAll(function(a){return c(a)}); -c(a)&&d.unshift(a);d.length?e.each(d,function(a){a.content=f(a.content,b);e.each(a._attributes,function(a){a.value=f(a.value,b)})}):(d=a.deepestChild()||a,d.content=h("abbreviationUtils").insertChildContent(d.content,b))}var a=h("abbreviationParser"),d="$#";a.addPreprocessor(function(a,b){if(b.pastedContent){var d=h("utils").splitByLines(b.pastedContent,!0);a.findAll(function(a){if(a.hasImplicitRepeat)return a.data("paste",d),a.repeatCount=d.length})}});a.addPostprocessor(function(a,d){!a.findAll(function(a){var d= -a.data("paste"),c="";e.isArray(d)?c=d[a.counter-1]:e.isFunction(d)?c=d(a.counter-1,a.content):d&&(c=d);c&&b(a,c);a.data("paste",null);return!e.isUndefined(d)}).length&&d.pastedContent&&b(a,d.pastedContent)})});emmet.exec(function(h,e){function g(f){var c=h("tagName");e.each(f.children,function(b){if(b.hasImplicitName()||b.data("forceNameResolving"))b._name=c.resolve(b.parent.name());g(b)});return f}h("abbreviationParser").addPostprocessor(g)}); -emmet.define("cssParser",function(h,e){function g(a){return typeof a!=="undefined"}function f(){return{"char":i.chnum,line:i.linenum}}function c(a,b,d){var c=i,d=d||{};j.push({charstart:g(d["char"])?d["char"]:c.chnum,charend:g(d.charend)?d.charend:c.chnum,linestart:g(d.line)?d.line:c.linenum,lineend:g(d.lineend)?d.lineend:c.linenum,value:a,type:b||a})}function b(a,b){var d=i,c=b||{},e=g(c["char"])?c["char"]:d.chnum,c=g(c.line)?c.line:d.linenum;return{name:"ParseError",message:a+" at line "+(c+1)+ -" char "+(e+1),walker:d,tokens:j}}function a(a){var b=i,d=b.ch,e=f(),j=a?a+d:d,d=b.nextChar();for(a&&(e["char"]-=a.length);n(d)||m(d);)j+=d,d=b.nextChar();c(j,"identifier",e)}function d(){var d=i.ch;if(d===" "||d==="\t"){for(var e=i.ch,j="",g=f();e===" "||e==="\t";)j+=e,e=i.nextChar();c(j,"white",g)}else{if(d==="/"){var e=i,d=g=e.ch,h,u=f();h=e.nextChar();if(h!=="*")u.charend=u["char"],u.lineend=u.line,j=c(d,d,u);else{for(;!(g==="*"&&h==="/");)d+=h,g=h,h=e.nextChar();d+=h;e.nextChar();c(d,"comment", -u)}return j}if(d==='"'||d==="'"){e=i;d=g=j=e.ch;u=f();for(j=e.nextChar();j!==g;){if(j==="\n")if(h=e.nextChar(),h==="\\")d+=j+h;else throw b("Unterminated string",u);else d+=j==="\\"?j+e.nextChar():j;j=e.nextChar()}d+=j;e.nextChar();c(d,"string",u)}else if(d==="("){e=i;j=e.ch;g=0;d=j;h=f();for(j=e.nextChar();j!==")"&&!g;){if(j==="(")g++;else if(j===")")g--;else if(j===!1)throw b("Unterminated brace",h);d+=j;j=e.nextChar()}d+=j;e.nextChar();c(d,"brace",h)}else{if(d==="-"||d==="."||m(d)){j=i;g=j.ch; -d=f();h=g;var u=h===".",z,g=j.nextChar();z=!m(g);if(u&&z)d.charend=d["char"],d.lineend=d.line,e=c(h,".",d);else if(h==="-"&&z)e=a("-");else{for(;g!==!1&&(m(g)||!u&&g===".");)g==="."&&(u=!0),h+=g,g=j.nextChar();c(h,"number",d)}return e}if(n(d))return a();if(l(d))return e=i,d=e.ch,j=f(),h=e.nextChar(),h==="="&&l(d,!0)?(d+=h,c(d,"match",j),e.nextChar(),g=void 0):(j.charend=j["char"]+1,j.lineend=j.line,c(d,d,j)),g;if(d==="\n")c("line"),i.nextChar();else throw b("Unrecognized character");}}}var i,j=[], -l,n,m;i={lines:null,total_lines:0,linenum:-1,line:"",ch:"",chnum:-1,init:function(a){var b=i;b.lines=a.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split("\n");b.total_lines=b.lines.length;b.chnum=-1;b.linenum=-1;b.ch="";b.line="";b.nextLine();b.nextChar()},nextLine:function(){this.linenum+=1;this.line=this.total_lines<=this.linenum?!1:this.lines[this.linenum];if(this.chnum!==-1)this.chnum=0;return this.line},nextChar:function(){for(this.chnum+=1;this.line.charAt(this.chnum)==="";){if(this.nextLine()=== -!1)return this.ch=!1;this.chnum=-1;return this.ch="\n"}return this.ch=this.line.charAt(this.chnum)},peek:function(){return this.line.charAt(this.chnum+1)}};n=function(a){return a==="_"||a==="-"||a>="a"&&a<="z"||a>="A"&&a<="Z"};m=function(a){return a!==!1&&a>="0"&&a<="9"};l=function(){for(var a="{}[]()+*=.,;:>~|\\%$#@^!".split(""),b="*^|$~".split(""),d={},c={},e=0;e")):null:a.match("--")?e(c("comment","--\>")):a.match("DOCTYPE",!0,!0)?(a.eatWhile(/[\w\._\-]/),e(b(1))):null;else if(a.eat("?"))return a.eatWhile(/[\w\._\-]/),d.tokenize=c("meta","?>"),"meta";else{A=a.eat("/")?"closeTag":"openTag";a.eatSpace();for(z="";i=a.eat(/[^\s\u00a0=<>\"\'\/?]/);)z+=i;d.tokenize=g;return"tag"}else return i== -"&"?(a.eat("#")?a.eat("x")?a.eatWhile(/[a-fA-F\d]/)&&a.eat(";"):a.eatWhile(/[\d]/)&&a.eat(";"):a.eatWhile(/[\w\.\-:]/)&&a.eat(";"))?"atom":"error":(a.eatWhile(/[^&<]/),"text")}function g(a,b){var d=a.next();return d==">"||d=="/"&&a.eat(">")?(b.tokenize=e,A=d==">"?"endTag":"selfcloseTag","tag"):d=="="?(A="equals",null):/[\'\"]/.test(d)?(b.tokenize=f(d),b.tokenize(a,b)):(a.eatWhile(/[^\s\u00a0=<>\"\'\/?]/),"word")}function f(a){return function(b,d){for(;!b.eol();)if(b.next()==a){d.tokenize=g;break}return"string"}} -function c(a,b){return function(d,c){for(;!d.eol();){if(d.match(b)){c.tokenize=e;break}d.next()}return a}}function b(a){return function(d,c){for(var i;(i=d.next())!=null;)if(i=="<")return c.tokenize=b(a+1),c.tokenize(d,c);else if(i==">")if(a==1){c.tokenize=e;break}else return c.tokenize=b(a-1),c.tokenize(d,c);return"meta"}}function a(){for(var a=arguments.length-1;a>=0;a--)k.cc.push(arguments[a])}function d(){a.apply(null,arguments);return!0}function i(){if(k.context)k.context=k.context.prev}function j(a){if(a== -"openTag")return k.tagName=z,d(o,l(k.startOfLine));else if(a=="closeTag")return a=!1,k.context?k.context.tagName!=z&&(u.implicitlyClosed.hasOwnProperty(k.context.tagName.toLowerCase())&&i(),a=!k.context||k.context.tagName!=z):a=!0,a&&(v="error"),d(n(a));return d()}function l(a){return function(b){if(b=="selfcloseTag"||b=="endTag"&&u.autoSelfClosers.hasOwnProperty(k.tagName.toLowerCase()))return m(k.tagName.toLowerCase()),d();if(b=="endTag"){m(k.tagName.toLowerCase());var b=k.tagName,c=u.doNotIndent.hasOwnProperty(b)|| -k.context&&k.context.noIndent;k.context={prev:k.context,tagName:b,indent:k.indented,startOfLine:a,noIndent:c}}return d()}}function n(a){return function(b){a&&(v="error");if(b=="endTag")return i(),d();v="error";return d(arguments.callee)}}function m(a){for(var b;;){if(!k.context)break;b=k.context.tagName.toLowerCase();if(!u.contextGrabbers.hasOwnProperty(b)||!u.contextGrabbers[b].hasOwnProperty(a))break;i()}}function o(b){if(b=="word")return v="attribute",d(q,o);if(b=="endTag"||b=="selfcloseTag")return a(); -v="error";return d(o)}function q(b){if(b=="equals")return d(r,o);u.allowMissing||(v="error");return b=="endTag"||b=="selfcloseTag"?a():d()}function r(b){if(b=="string")return d(t);if(b=="word"&&u.allowUnquoted)return v="string",d();v="error";return b=="endTag"||b=="selfCloseTag"?a():d()}function t(b){return b=="string"?d(t):a()}function x(a,b){if(a.sol())b.startOfLine=!0,b.indented=0;if(a.eatSpace())return null;v=A=z=null;var d=b.tokenize(a,b);b.type=A;if((d||A)&&d!="comment")for(k=b;;)if((b.cc.pop()|| -j)(A||d))break;b.startOfLine=!1;return v||d}var u={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!0,allowMissing:!0},z=null,A=null,k=null,v;return{parse:function(a,b){for(var b=b||0,d={tokenize:e,cc:[],indented:0,startOfLine:!0,tagName:null,context:null},c=h("stringStream").create(a),i=[];!c.eol();)i.push({type:x(c,d),start:c.start+b,end:c.pos+b}),c.start=c.pos;return i}}}); -emmet.define("utils",function(h,e){function g(c){this._data=[];this.length=0;c&&this.append(c)}var f="${0}";g.prototype={append:function(c){this._data.push(c);this.length+=c.length},toString:function(){return this._data.join("")},valueOf:function(){return this.toString()}};return{reTag:/<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/,endsWithTag:function(c){return this.reTag.test(c)},isNumeric:function(c){typeof c=="string"&&(c=c.charCodeAt(0));return c&&c>47&& -c<58},trim:function(c){return(c||"").replace(/^\s+|\s+$/g,"")},getNewline:function(){var c=h("resources");if(!c)return"\n";c=c.getVariable("newline");return e.isString(c)?c:"\n"},setNewline:function(c){var b=h("resources");b.setVariable("newline",c);b.setVariable("nl",c)},splitByLines:function(c,b){var a=this.getNewline(),a=(c||"").replace(/\r\n/g,"\n").replace(/\n\r/g,"\n").replace(/\r/g,"\n").replace(/\n/g,a).split(a);b&&(a=e.filter(a,function(a){return a.length&&!!this.trim(a)},this));return a}, -normalizeNewline:function(c){return this.splitByLines(c).join(this.getNewline())},repeatString:function(c,b){for(var a=[],d=0;dd++;)a+="0";return a+c},unindentString:function(c, -b){for(var a=this.splitByLines(c),d=0;dc.length?c:c.substring(0,a)+b+c.substring(d)},narrowToNonSpace:function(c,b,a){b=h("range").create(b,a);for(a=/[\s\n\r\u00a0]/;b.start -b.start;)if(b.end--,!a.test(c.charAt(b.end))){b.end++;break}return b},findNewlineBounds:function(c,b){for(var a=c.length,d=0,e=a-1,j=b-1;j>0;j--){var f=c.charAt(j);if(f=="\n"||f=="\r"){d=j+1;break}}for(j=b;j=this.start}, -intersection:function(e){if(this.overlap(e)){var c=Math.max(e.start,this.start);return new g(c,Math.min(e.end,this.end)-c)}return null},union:function(e){if(this.overlap(e)){var c=Math.min(e.start,this.start);return new g(c,Math.max(e.end,this.end)-c)}return null},inside:function(e){return this.start<=e&&this.end>e},include:function(e){return this.start<=e.start&&this.end>=e.end},substring:function(e){return this.length()>0?e.substring(this.start,this.end):""},clone:function(){return new g(this.start, -this.length())},toArray:function(){return[this.start,this.end]},toString:function(){return"{"+this.start+", "+this.length()+"}"}};return{create:function(f,c){return e.isUndefined(f)||f===null?null:f instanceof g?f:new g(f,c)},create2:function(f,c){e.isNumber(f)&&e.isNumber(c)&&(c-=f);return this.create(f,c)}}}); -emmet.define("handlerList",function(h,e){function g(){this._list=[]}g.prototype={add:function(f,c){this._list.push(e.extend({order:0},c||{},{fn:f}))},remove:function(f){this._list=e.without(this._list,e.find(this._list,function(c){return c.fn===f}))},list:function(){return e.sortBy(this._list,"order").reverse()},listFn:function(){return e.pluck(this.list(),"fn")},exec:function(f,c){var c=c||[],b=null;e.find(this.list(),function(a){b=a.fn.apply(a,c);if(b!==f)return!0});return b}};return{create:function(){return new g}}}); -emmet.define("tokenIterator",function(h,e){function g(e){this.tokens=e;this._position=0;this.reset()}g.prototype={next:function(){if(this.hasNext()){var e=this.tokens[++this._i];this._position=e.start;return e}return null},current:function(){return this.tokens[this._i]},position:function(){return this._position},hasNext:function(){return this._i=this.string.length},sol:function(){return this.pos==0},peek:function(){return this.string.charAt(this.pos)},next:function(){if(this.posg},eatSpace:function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},skipToEnd:function(){this.pos=this.string.length},skipTo:function(e){e=this.string.indexOf(e,this.pos);if(e>-1)return this.pos=e,!0},skipToPair:function(e,g){for(var f=0,c,b=this.pos,a=this.string.length;b/,i={},j={},l=h("handlerList").create();return{setVocabulary:function(a,d){d==b?i=a:j=a},getVocabulary:g,getResource:function(d,e,i){return c(a,d,e,i)||c(b,d,e,i)},getAbbreviation:function(a,b){b=b||"";return this.getResource(a,"abbreviations",b)||this.getResource(a,"abbreviations",b.replace(/\-/g,":"))},getSnippet:function(a,b){b=b||"";return this.getResource(a,"snippets",b)||this.getResource(a,"snippets",b.replace(/\-/g, -":"))},getMatchedResource:function(a,b){return l.exec(null,e.toArray(arguments))||this.getAbbreviation(b,a.name())||this.getSnippet(b,a.name())},getVariable:function(d){return f(a,"variables",d)[0]||f(b,"variables",d)[0]},setVariable:function(a,b){var d=g("user")||{};if(!("variables"in d))d.variables={};d.variables[a]=b;this.setVocabulary(d,"user")},getSubset:function(d,c){return f(a,d,c)[0]||f(b,d,c)[0]},hasSyntax:function(d){return d in g(a)||d in g(b)},addResolver:function(a,b){l.add(a,b)},removeResolver:function(a){l.remove(a)}}}); -emmet.define("actions",function(h,e){function g(c){return h("utils").trim(c.charAt(0).toUpperCase()+c.substring(1).replace(/_[a-z]/g,function(b){return" "+b.charAt(1).toUpperCase()}))}var f={};return{add:function(c,b,a){c=c.toLowerCase();a=a||{};if(!a.label)a.label=g(c);f[c]={name:c,fn:b,options:a}},get:function(c){return f[c.toLowerCase()]},run:function(c,b){e.isArray(b)||(b=e.rest(arguments));var a=this.get(c);return a?a.fn.apply(emmet,b):(emmet.log('Action "%s" is not defined',c),!1)},getAll:function(){return f}, -getList:function(){return e.values(this.getAll())},getMenu:function(c){var b=[],c=c||[];e.each(this.getList(),function(a){if(!a.options.hidden&&!e.include(c,a.name)){var d=g(a.name),i=b;if(a.options.label)for(var j=a.options.label.split("/"),d=j.pop(),f,h;f=j.shift();)h=e.find(i,function(a){return a.type=="submenu"&&a.name==f}),h||(h={name:f,type:"submenu",items:[]},i.push(h)),i=h.items;i.push({type:"action",name:a.name,label:d})}});return b},getActionNameForMenuTitle:function(c,b){var a=null;e.find(b|| -this.getMenu(),function(b){if(b.type=="action"){if(b.label==c||b.name==c)return a=b.name}else return a=this.getActionNameForMenuTitle(c,b.items)},this);return a||null}}}); -emmet.define("profile",function(h,e){function g(b){e.extend(this,a,b)}function f(a,b){switch(String(b||"").toLowerCase()){case "lower":return a.toLowerCase();case "upper":return a.toUpperCase()}return a}function c(a,c){return b[a.toLowerCase()]=new g(c)}var b={},a={tag_case:"asis",attr_case:"asis",attr_quotes:"double",tag_nl:"decide",tag_nl_leaf:!1,place_cursor:!0,indent:!0,inline_break:3,self_closing_tag:"xhtml",filters:""};g.prototype={tagName:function(a){return f(a,this.tag_case)},attributeName:function(a){return f(a, -this.attr_case)},attributeQuote:function(){return this.attr_quotes=="single"?"'":'"'},selfClosing:function(){return this.self_closing_tag=="xhtml"?" /":this.self_closing_tag===!0?"/":""},cursor:function(){return this.place_cursor?h("utils").getCaretPlaceholder():""}};c("xhtml");c("html",{self_closing_tag:!1});c("xml",{self_closing_tag:!0,tag_nl:!0});c("plain",{tag_nl:!1,indent:!1,place_cursor:!1});c("line",{tag_nl:!1,indent:!1});return{create:function(b,i){return arguments.length==2?c(b,i):new g(e.defaults(b|| -{},a))},get:function(a,c){if(c&&e.isString(a)){var j=h("resources").getSubset(c,"profile");j&&(a=j)}return!a?b.plain:a instanceof g?a:e.isString(a)&&a.toLowerCase()in b?b[a.toLowerCase()]:this.create(a)},remove:function(a){a=(a||"").toLowerCase();a in b&&delete b[a]},stringCase:f}}); -emmet.define("editorUtils",function(h){return{isInsideTag:function(e,g){for(var f=/^<\/?\w[\w\:\-]*.*?>/,c=g;c>-1;){if(e.charAt(c)=="<")break;c--}return c!=-1&&(f=f.exec(e.substring(c)))&&g>c&&g"&&d.endsWithTag(e.substring(0,g+1)))){f=g+1;break}}return f!=-1&&!a&&!b&&!c?e.substring(f):""},getImageSize:function(e){var g=function(){return e.charCodeAt(f++)};if(e.substr(0,8)==="\u0089PNG\r\n\u001a\n"){var f=e.indexOf("IHDR")+4;return{width:g()<<24|g()<<16|g()<<8|g(),height:g()<<24|g()<<16|g()<<8|g()}}else if(e.substr(0,4)==="GIF8")return f=6,{width:g()|g()<<8,height:g()|g()<<8};else if(e.substr(0,2)==="\u00ff\u00d8")for(var f=2,c=e.length;f=192&&b<=207&&!(b&4)&&!(b&8))return f+=1,{height:g()<<8|g(),width:g()<<8|g()};else f+=a-2}},captureContext:function(e){if(String(e.getSyntax())in{html:1,xml:1,xsl:1}){var g=h("html_matcher").getTags(String(e.getContent()),e.getCaretPos(),String(e.getProfileName()));if(g&&g[0]&&g[0].type=="tag"){for(var e=/([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,f=g[0],g=f.full_tag.replace(/^<[\w\-\:]+/,""),f={name:f.name,attributes:[]}, -c;c=e.exec(g);)f.attributes.push({name:c[1],value:c[2]});return f}}return null},findExpressionBounds:function(e,g){for(var f=String(e.getContent()),c=f.length,b=e.getCaretPos()-1,a=b+1;b>=0&&g(f.charAt(b),b,f);)b--;for(;ab)return h("range").create([++b,a])},compoundUpdate:function(e,g){if(g){var f=e.getSelectionRange();e.replaceContent(g.data,g.start,g.end,!0);e.createSelection(g.caret,g.caret+f.end-f.start);return!0}return!1}}}); -emmet.define("abbreviationUtils",function(h,e){return{isSnippet:function(e){return h("elements").is(e.matchedResource(),"snippet")},isUnary:function(e){var f=e.matchedResource();return e.children.length||this.isSnippet(e)?!1:f&&f.is_empty||h("tagName").isEmptyElement(e.name())},isInline:function(e){return e.isTextNode()||!e.name()||h("tagName").isInlineLevel(e.name())},isBlock:function(e){return h("elements").is(e.matchedResource(),"snippet")||!this.isInline(e)},hasTagsInContent:function(e){return h("utils").matchesTag(e.content)}, -hasBlockChildren:function(h){return this.hasTagsInContent(h)&&this.isBlock(h)||e.any(h.children,function(e){return this.isBlock(e)},this)},insertChildContent:function(g,f,c){var c=e.extend({keepVariable:!0,appendIfNoChild:!0},c||{}),b=!1,a=h("utils"),g=a.replaceVariables(g,function(d,e,j){var h=d;e=="child"&&(h=a.padString(f,a.getLinePaddingFromPosition(g,j.start)),b=!0,c.keepVariable&&(h+=d));return h});!b&&c.appendIfNoChild&&(g+=f);return g}}}); -emmet.define("base64",function(){return{encode:function(h){for(var e=[],g,f,c,b,a,d,i=0,j=h.length;i>2,g=(g&3)<<4|f>>4,f=(f&15)<<2|c>>6,c&=63,isNaN(a)?f=c=64:isNaN(d)&&(c=64),e.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(g)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+ -"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(c));return e.join("")},decode:function(h){var e,g,f,c,b,a=0,d=0,i=[],j=h.length;if(!h)return h;h+="";do e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)), -f=e<<18|g<<12|c<<6|b,e=f>>16&255,g=f>>8&255,f&=255,c==64?i[d++]=String.fromCharCode(e):b==64?i[d++]=String.fromCharCode(e,g):i[d++]=String.fromCharCode(e,g,f);while(ad||b.start<=d&& -b.end>d?(c=a.start,e=b.end):(c=a.end,e=b.start);return[c,e]}function b(a,b,d){m.opening_tag=a;m.closing_tag=b;a=c(a,b,d||0);m.start_ix=a[0];m.end_ix=a[1];return m.start_ix!=-1?[m.start_ix,m.end_ix]:null}function a(a,b,j,f){function n(b,d){arguments.length==1&&(d=w);return a.substr(d,b.length)==b}function m(b){for(;b--;)if(a.charAt(b)=="<"&&n("<\!--",b))break;return b}f=f||c;h(j);var j=[],k=[],l=null,q=null,o=a.length,s,w;j.last=k.last=function(){return this[this.length-1]};for(w=b;w--&&w>=0;)if(s= -a.charAt(w),s=="<"){var y=a.substring(w,o);if(s=y.match(i))s=e(s,w),s.startb?q=s:k.push(s);else if(s=y.match(d))if(s=e(s,w),s.unary){if(s.startb)return f(s,null,b)}else if(k.last()&&k.last().name==s.name)k.pop();else{l=s;break}else if(y.indexOf("<\!--")==0&&(s=y.search("--\>")+w+3,w=b))return f(g(w,s))}else s=="-"&&n("--\>")&&(w=m(w));if(!l)return f(null);if(!q)for(w=b;w")+2);else if(s=="-"&&n("--\>")&&(!j.last()||j.last().type!="comment"))return s=w+3,f(g(m(w),s));return f(l,q,b)}var d=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,i=/^<\/([\w\:\-]+)[^>]*>/,j=f("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");f("address,applet,blockquote,button,center,dd,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul"); -f("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");var l=f("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr"),n="xhtml",m={opening_tag:null,closing_tag:null,start_ix:-1,end_ix:-1},o=function(d,c,e){return a(d,c,e,b)};o.start_tag=d;o.end_tag=i;o.find=function(b,d,c){return a(b,d,c)};o.getTags=function(b,d,c){return a(b,d,c,function(a,b){return[a,b]})};o.last_match= -m;try{emmet.define("html_matcher",function(){return o})}catch(q){}})(); -emmet.define("tabStops",function(h,e){var g=100,f=0,c={replaceCarets:!1,escape:function(b){return"\\"+b},tabstop:function(b){return b.token},variable:function(b){return b.token}};h("abbreviationParser").addOutputProcessor(function(b,a){var d=0,c=h("tabStops"),e=h("utils"),b=c.processText(b,{tabstop:function(a){var b=parseInt(a.group);if(b==0)return"${0}";b>d&&(d=b);return a.placeholder?"${"+(b+f)+":"+a.placeholder+"}":"${"+(b+f)+"}"}}),b=e.replaceVariables(b,c.variablesResolver(a));f+=d+1;return b}); -return{extract:function(b,a){var d=h("utils"),i={carets:""},j=[],a=e.extend({},c,a,{tabstop:function(a){var b=a.token,d="";if(a.placeholder=="cursor")j.push({start:a.start,end:a.start+b.length,group:"carets",value:""});else{if("placeholder"in a)i[a.group]=a.placeholder;a.group in i&&(d=i[a.group]);j.push({start:a.start,end:a.start+b.length,group:a.group,value:d})}return b}});a.replaceCarets&&(b=b.replace(RegExp(d.escapeForRegexp(d.getCaretPlaceholder()),"g"),"${0:cursor}"));var b=this.processText(b, -a),f=d.stringBuilder(),g=0,d=e.map(j,function(a){f.append(b.substring(g,a.start));var d=f.length,c=i[a.group]||"";f.append(c);g=a.end;return{group:a.group,start:d,end:d+c.length}});f.append(b.substring(g));return{text:f.toString(),tabstops:e.sortBy(d,"start")}},processText:function(b,a){for(var a=e.extend({},c,a),d=h("utils").stringBuilder(),i=h("stringStream").create(b),j,f;j=i.next();)if(j=="\\"&&!i.eol())d.append(a.escape(i.next()));else{f=j;if(j=="$")if(i.start=i.pos-1,i.match(/^[0-9]+/))f=a.tabstop({start:d.length, -group:i.current().substr(1),token:i.current()});else if(j=i.match(/^\{([a-z_\-][\w\-]*)\}/))f=a.variable({start:d.length,name:j[1],token:i.current()});else if(j=i.match(/^\{([0-9]+)(:.+?)?\}/)){f={start:d.length,group:j[1],token:i.current()};if(j[2])f.placeholder=j[2].substr(1);f=a.tabstop(f)}d.append(f)}return d.toString()},upgrade:function(b,a){var d=0,c={tabstop:function(b){var c=parseInt(b.group);c>d&&(d=c);return b.placeholder?"${"+(c+a)+":"+b.placeholder+"}":"${"+(c+a)+"}"}};e.each(["start", -"end","content"],function(a){b[a]=this.processText(b[a],c)},this);return d},variablesResolver:function(b){var a={},d=h("resources");return function(c,f){if(f=="child")return c;if(f=="cursor")return h("utils").getCaretPlaceholder();var l=b.attribute(f);if(!e.isUndefined(l))return l;if(l=d.getVariable(f))return l;a[f]||(a[f]=g++);return"${"+a[f]+":"+f+"}"}},resetPlaceholderCounter:function(){console.log("deprecated");g=100},resetTabstopIndex:function(){f=0;g=100}}}); -emmet.define("preferences",function(h,e){var g={},f={},c=null,b=null;return{define:function(a,b,c){var j=a;e.isString(a)&&(j={},j[a]={value:b,description:c});e.each(j,function(a,b){f[b]=e.isObject(a)&&"value"in a&&e.keys(a).length<3?a:{value:a}})},set:function(a,b){var c=a;e.isString(a)&&(c={},c[a]=b);e.each(c,function(a,b){if(!(b in f))throw'Property "'+b+'" is not defined. You should define it first with `define` method of current module';if(a!==f[b].value){switch(typeof f[b].value){case "boolean":var d= -a;e.isString(d)?(d=d.toLowerCase(),a=d=="yes"||d=="true"||d=="1"):a=!!d;break;case "number":a=parseInt(a+"",10)||0;break;default:a+=""}g[b]=a}else b in g&&delete g[p]})},get:function(a){if(a in g)return g[a];if(a in f)return f[a].value},getArray:function(a){a=this.get(a);e.isUndefined(a)||(a=e.map(a.split(","),h("utils").trim),a.length||(a=null));return a},description:function(a){return a in f?f[a].description:void 0},remove:function(a){e.isArray(a)||(a=[a]);e.each(a,function(a){a in g&&delete g[a]; -a in f&&delete f[a]})},list:function(){return e.map(e.keys(f).sort(),function(a){return{name:a,value:this.get(a),type:typeof f[a].value,description:f[a].description}},this)},load:function(a){e.each(a,function(a,b){this.set(b,a)},this)},exportModified:function(){return e.clone(g)},reset:function(){g={}},_startTest:function(){c=f;b=g;f={};g={}},_stopTest:function(){f=c;g=b}}}); -emmet.define("filters",function(h,e){function g(c){return!c?[]:e.isString(c)?c.split(/[\|,]/g):c}var f={};return{add:function(c,b){f[c]=b},apply:function(c,b,a){var d=h("utils"),a=h("profile").get(a);e.each(g(b),function(b){(b=d.trim(b.toLowerCase()))&&b in f&&(c=f[b](c,a))});return c},composeList:function(c,b,a){b=h("profile").get(b);c=g(b.filters||h("resources").getSubset(c,"filters")||"html");a&&(c=c.concat(g(a)));if(!c||!c.length)c=g("html");return c},extractFromAbbreviation:function(c){var b= -"",c=c.replace(/\|([\w\|\-]+)$/,function(a,d){b=d;return""});return[c,g(b)]}}}); -emmet.define("elements",function(h,e){function g(a){return{data:a}}var f={},c=/([\w\-]+)\s*=\s*(['"])(.*?)\2/g,b={add:function(a,b){var c=this;f[a]=function(){var e=b.apply(c,arguments);if(e)e.type=a;return e}},get:function(a){return f[a]},create:function(a){var b=[].slice.call(arguments,1),c=this.get(a);return c?c.apply(this,b):null},is:function(a,b){return a&&a.type===b}};b.add("element",function(a,b,f){var h={name:a,is_empty:!!f};if(b)if(h.attributes=[],e.isArray(b))h.attributes=b;else if(e.isString(b))for(;a= -c.exec(b);)h.attributes.push({name:a[1],value:a[3]});else e.each(b,function(a,b){h.attributes.push({name:b,value:a})});return h});b.add("snippet",g);b.add("reference",g);b.add("empty",function(){return{}});return b}); -emmet.define("editTree",function(h,e,g){function f(a,b){this.options=e.extend({offset:0},b);this.source=a;this._children=[];this._positions={name:0};this.initialize.apply(this,arguments)}function c(a,b,c){this.parent=a;this._name=b.value;this._value=c?c.value:"";this._positions={name:b.start,value:c?c.start:-1};this.initialize.apply(this,arguments)}var b=h("range").create;f.extend=g.extend;f.prototype={initialize:function(){},_updateSource:function(a,d,c){var f=b(d,e.isUndefined(c)?0:c-d),g=a.length- -f.length(),n=function(a){e.each(a,function(b,d){b>=f.end&&(a[d]+=g)})};n(this._positions);e.each(this.list(),function(a){n(a._positions)});this.source=h("utils").replaceSubstring(this.source,a,f)},add:function(a,b){var e=new c(a,b);this._children.push(e);return e},get:function(a){return e.isNumber(a)?this.list()[a]:e.isString(a)?e.find(this.list(),function(b){return b.name()===a}):a},getAll:function(a){e.isArray(a)||(a=[a]);var b=[],c=[];e.each(a,function(a){e.isString(a)?b.push(a):e.isNumber(a)&& -c.push(a)});return e.filter(this.list(),function(a,f){return e.include(c,f)||e.include(b,a.name())})},value:function(a,b,c){var f=this.get(a);if(f)return f.value(b);if(!e.isUndefined(b))return this.add(a,b,c)},values:function(a){return e.map(this.getAll(a),function(a){return a.value()})},remove:function(a){if(a=this.get(a))this._updateSource("",a.fullRange()),this._children=e.without(this._children,a)},list:function(){return this._children},indexOf:function(a){return e.indexOf(this.list(),this.get(a))}, -name:function(a){if(!e.isUndefined(a)&&this._name!==(a=String(a)))this._updateSource(a,this._positions.name,this._positions.name+this._name.length),this._name=a;return this._name},nameRange:function(a){return b(this._positions.name+(a?this.options.offset:0),this.name())},range:function(a){return b(a?this.options.offset:0,this.toString())},itemFromPosition:function(a,b){return e.find(this.list(),function(c){return c.range(b).inside(a)})},toString:function(){return this.source}};c.extend=g.extend;c.prototype= -{initialize:function(){},_pos:function(a,b){return a+(b?this.parent.options.offset:0)},value:function(a){if(!e.isUndefined(a)&&this._value!==(a=String(a)))this.parent._updateSource(a,this.valueRange()),this._value=a;return this._value},name:function(a){if(!e.isUndefined(a)&&this._name!==(a=String(a)))this.parent._updateSource(a,this.nameRange()),this._name=a;return this._name},namePosition:function(a){return this._pos(this._positions.name,a)},valuePosition:function(a){return this._pos(this._positions.value, -a)},range:function(a){return b(this.namePosition(a),this.toString())},fullRange:function(a){return this.range(a)},nameRange:function(a){return b(this.namePosition(a),this.name())},valueRange:function(a){return b(this.valuePosition(a),this.value())},toString:function(){return this.name()+this.value()},valueOf:function(){return this.toString()}};return{EditContainer:f,EditElement:c,createToken:function(a,b,c){a={start:a||0,value:b||"",type:c};a.end=a.start+a.value.length;return a}}}); -emmet.define("cssEditTree",function(h,e){function g(a,b){return h("range").create(a,b)}function f(a,b){var b=b||d|i,c=["white","line"];if((b&i)==i)for(;a.length&&e.include(c,e.last(a).type);)a.pop();if((b&d)==d)for(;a.length&&e.include(c,a[0].type);)a.shift();return a}function c(a){var b=["white","line",":"],c=[],h,j;a.nextUntil(function(){return!e.include(b,this.itemNext().type)});for(j=a.current().end;h=a.next();){if(h.type=="}"||h.type==";")return f(c,d|(h.type=="}"?i:0)),c.length?(j=c[0].start, -a=e.last(c).end):a=j,g(j,a-j);c.push(h)}if(c.length)return g(c[0].start,e.last(c).end-c[0].start)}function b(a){var b=h("stringStream").create(a),d=[],c=/[\s\u00a0,]/,f=function(){b.next();d.push(g(b.start,b.current()));b.start=b.pos};b.eatSpace();for(b.start=b.pos;a=b.next();)if(a=='"'||a=="'"){b.next();if(!b.skipTo(a))break;f()}else if(a=="("){b.backUp(1);if(!b.skipToPair("(",")"))break;b.backUp(1);f()}else if(c.test(a))d.push(g(b.start,b.current().length-1)),b.eatWhile(c),b.start=b.pos;f();return e.chain(d).filter(function(a){return!!a.length()}).uniq(!1, -function(a){return a.toString()}).value()}var a={styleBefore:"\n\t",styleSeparator:": ",offset:0},d=1,i=2,j=h("editTree").EditContainer.extend({initialize:function(b){e.defaults(this.options,a);var d=h("editTree"),i=h("tokenIterator").create(h("cssParser").parse(b)),j,r=[],t;for(j=i.position();t=i.next();){if(t.type=="{")break;r.push(t)}f(r);r.length?(j=r[0].start,r=e.last(r).end):r=j;j=g(j,r-j);this._positions.name=j.start;this._name=j.substring(b);if(!i.current()||i.current().type!="{")throw"Invalid CSS rule"; -for(this._positions.contentStart=i.position()+1;j=i.next();){if(r=j.type=="identifier")a:{r=i.tokens;t=i._i+1;for(var x=r.length;t1)c.styleBefore="\n"+e.last(f);c.styleSeparator=b.substring(c.nameRange().end,c.valuePosition());c.styleBefore=e.last(c.styleBefore.split("*/"));c.styleSeparator=c.styleSeparator.replace(/\/\*.*?\*\//g,"");a=c.range().end})},add:function(a,b,d){var c=this.list(),f=this._positions.contentStart,i=e.pick(this.options,"styleBefore", -"styleSeparator"),j=h("editTree");if(e.isUndefined(d))d=c.length;var g=c[d];if(g)f=g.fullRange().start;else if(g=c[d-1])g.end(";"),f=g.range().end;g&&(i=e.pick(g,"styleBefore","styleSeparator"));a=j.createToken(f+i.styleBefore.length,a);b=j.createToken(a.end+i.styleSeparator.length,b);j=new l(this,a,b,j.createToken(b.end,";"));e.extend(j,i);this._updateSource(j.styleBefore+j.toString(),f);this._children.splice(d,0,j);return j}}),l=h("editTree").EditElement.extend({initialize:function(a,b,d,c){this.styleBefore= -a.options.styleBefore;this.styleSeparator=a.options.styleSeparator;this._end=c.value;this._positions.end=c.start},valueParts:function(a){var d=b(this.value());if(a){var c=this.valuePosition(!0);e.each(d,function(a){a.shift(c)})}return d},end:function(a){if(!e.isUndefined(a)&&this._end!==a)this.parent._updateSource(a,this._positions.end,this._positions.end+this._end.length),this._end=a;return this._end},fullRange:function(a){a=this.range(a);a.start-=this.styleBefore.length;return a},toString:function(){return this.name()+ -this.styleSeparator+this.value()+this.end()}});return{parse:function(a,b){return new j(a,b)},parseFromPosition:function(a,b,d){d=this.extractRule(a,b,d);return!d||!d.inside(b)?null:this.parse(d.substring(a),{offset:d.start})},extractRule:function(a,b,d){for(var c="",e=a.length,f=-1,i;b>=0;){i=a.charAt(b);if(i=="{"){f=b;break}else if(i=="}"&&!d){b++;break}b--}for(;b=0;){i=a.charAt(b);if("{}/\\<>".indexOf(i)!= --1)break;b--}d=a.substring(b+1,f).replace(/^[\s\n\r]+/m,"");return h("range").create(f-d.length,c.length+d.length)}return null},baseName:function(a){return a.replace(/^\s*\-\w+\-/,"")},findParts:b}}); -emmet.define("xmlEditTree",function(h,e){var g={styleBefore:" ",styleSeparator:"=",styleQuote:'"',offset:0},f=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/m,c=h("editTree").EditContainer.extend({initialize:function(a){e.defaults(this.options,g);this._positions.name=1;var d=null,c=h("xmlParser").parse(a),f=h("range");e.each(c,function(c){c.value=f.create(c).substring(a);switch(c.type){case "tag":if(/^<[^\/]+/.test(c.value))this._name=c.value.substring(1); -break;case "attribute":d&&this._children.push(new b(this,d));d=c;break;case "string":this._children.push(new b(this,d,c)),d=null}},this);d&&this._children.push(new b(this,d));this._saveStyle()},_saveStyle:function(){var a=this.nameRange().end,b=this.source;e.each(this.list(),function(c){c.styleBefore=b.substring(a,c.namePosition());if(c.valuePosition()!==-1)c.styleSeparator=b.substring(c.namePosition()+c.name().length,c.valuePosition()-c.styleQuote.length);a=c.range().end})},add:function(a,d,c){var f= -this.list(),g=this.nameRange().end,n=h("editTree"),m=e.pick(this.options,"styleBefore","styleSeparator","styleQuote");if(e.isUndefined(c))c=f.length;var o=f[c];if(o)g=o.fullRange().start;else if(o=f[c-1])g=o.range().end;o&&(m=e.pick(o,"styleBefore","styleSeparator","styleQuote"));d=m.styleQuote+d+m.styleQuote;a=new b(this,n.createToken(g+m.styleBefore.length,a),n.createToken(g+m.styleBefore.length+a.length+m.styleSeparator.length,d));e.extend(a,m);this._updateSource(a.styleBefore+a.toString(),g); -this._children.splice(c,0,a);return a}}),b=h("editTree").EditElement.extend({initialize:function(a,b,c){this.styleBefore=a.options.styleBefore;this.styleSeparator=a.options.styleSeparator;b="";a=a.options.styleQuote;if(c)b=c.value,a=b.charAt(0),a=='"'||a=="'"?b=b.substring(1):a="",a&&b.charAt(b.length-1)==a&&(b=b.substring(0,b.length-1));this.styleQuote=a;this._value=b;this._positions.value=c?c.start+a.length:-1},fullRange:function(a){a=this.range(a);a.start-=this.styleBefore.length;return a},toString:function(){return this.name()+ -this.styleSeparator+this.styleQuote+this.value()+this.styleQuote}});return{parse:function(a,b){return new c(a,b)},parseFromPosition:function(a,b,c){c=this.extractTag(a,b,c);return!c||!c.inside(b)?null:this.parse(c.substring(a),{offset:c.start})},extractTag:function(a,b,c){var e=a.length,g,n=h("range"),m=Math.min(2E3,e),o=null,q=function(b){var c;if(a.charAt(b)=="<"&&(c=a.substr(b,m).match(f)))return n.create(b,c[0])};for(g=b;g>=0;g--)if(o=q(g))break;if(o&&(o.inside(b)||c))return o;if(!o&&c)return null; -for(g=b;g=a++;)if(b.substr(a,e.length)==e){g=a+e.length;break}}return f!=-1&&g!=-1?h("range").create(f,g-f):null}function c(b,a,c,e){function g(b){return b.replace(RegExp("^"+q.escapeForRegexp(a)+"\\s*"), -function(a){m-=a.length;return""}).replace(RegExp("\\s*"+q.escapeForRegexp(c)+"$"),"")}var l=h("editorUtils"),n=l.outputInfo(b).content,m=b.getCaretPos(),o=null,q=h("utils");(o=f(n,m,a,c))&&o.overlap(e)?(e=o,o=g(e.substring(n))):(o=a+" "+e.substring(n).replace(RegExp(q.escapeForRegexp(a)+"\\s*|\\s*"+q.escapeForRegexp(c),"g"),"")+" "+c,m+=a.length+1);return o!==null?(b.setCaretPos(e.start),b.replaceContent(l.unindent(b,o),e.start,e.end),b.setCaretPos(m),!0):!1}h("actions").add("toggle_comment",function(b){var a= -h("editorUtils").outputInfo(b);if(a.syntax=="css"){var d=b.getCaretPos(),e=h("html_matcher").getTags(a.content,d);if(e&&e[0]&&e[0].type=="tag"&&e[0].start<=d&&e[0].end>=d)a.syntax="html"}if(a.syntax=="css"){e=h("range").create(b.getSelectionRange());a=h("editorUtils").outputInfo(b);if(!e.length()&&(d=h("cssEditTree").parseFromPosition(a.content,b.getCaretPos())))e=(e=g(d,b.getCaretPos()))?e.range(!0):h("range").create(d.nameRange(!0).start,d.source);e.length()||(e=h("range").create(b.getCurrentLineRange()), -h("utils").narrowToNonSpace(a.content,e));b=c(b,"/*","*/",e)}else{a=h("range").create(b.getSelectionRange());d=h("editorUtils").outputInfo(b);if(!a.length()&&(d=h("html_matcher").getTags(d.content,b.getCaretPos(),d.profile))&&d[0])a.start=d[0].start,a.end=d[1]?d[1].end:d[0].end;b=c(b,"<\!--","--\>",a)}return b})}); -emmet.exec(function(h){function e(e,f,c){function b(b){for(var c=b;c>=0;){var d=a.charAt(c);if(d=="\n"||d=="\r")break;c--}return a.substring(c,b)}for(var f=f||1,c=e.getCaretPos()+(c||0),a=String(e.getContent()),e=a.length,d=-1,h=/^\s+$/;c<=e&&c>=0;){c+=f;var j=a.charAt(c),l=a.charAt(c+1),n=a.charAt(c-1);switch(j){case '"':case "'":l==j&&n=="="&&(d=c+1);break;case ">":l=="<"&&(d=c+1);break;case "\n":case "\r":h.test(b(c-1))&&(d=c)}if(d!=-1)break}return d}h=h("actions");h.add("prev_edit_point",function(g){var f= -g.getCaretPos(),c=e(g,-1);c==f&&(c=e(g,-1,-2));return c!=-1?(g.setCaretPos(c),!0):!1},{label:"Previous Edit Point"});h.add("next_edit_point",function(g){var f=e(g,1);f!=-1&&g.setCaretPos(f)})}); -emmet.exec(function(h,e){function g(a,b,c,d){var e=h("range"),f=h("editorUtils").outputInfo(a).content,g=f.length,j,i=e.create(-1,0),l=e.create(a.getSelectionRange());j=l.start;for(var n=1E5;j>=0&&j0;){if(e=c(f,j,b)){if(i.equal(e))break;i=e.clone();if(j=d(e.substring(f),e.start,l.clone()))return a.createSelection(j.start,j.end),!0;else j=b?e.start:e.end-1}j+=b?-1:1}return!1}function f(a){var b=!0;return g(a,!1,function(a,c){if(b){b=!1;var d;a:{d=c;for(var e;d>=0;){if(e=i(a,d)){d=e;break a}d--}d= -null}return d}else return i(a,c)},function(a,b,c){return d(a,b,c,!1)})}function c(a){return g(a,!0,i,function(a,b,c){return d(a,b,c,!0)})}function b(b,c,d){var d=d||0,f=h("range"),g=[],i=-1,l="",n="",q,m;e.each(c,function(c){switch(c.type){case "tag":m=b.substring(c.start,c.end);/^<[\w\:\-]/.test(m)&&g.push(f.create({start:c.start+1,end:c.end}));break;case "attribute":i=c.start;l=b.substring(c.start,c.end);break;case "string":g.push(f.create(i,c.end-i)),q=f.create(c),n=q.substring(b),j(n.charAt(0))&& -q.start++,j(n.charAt(n.length-1))&&q.end--,g.push(q),l=="class"&&(g=g.concat(a(q.substring(b),q.start)))}});e.each(g,function(a){a.shift(d)});return e.chain(g).filter(function(a){return!!a.length()}).uniq(!1,function(a){return a.toString()}).value()}function a(a,b){var b=b||0,c=[],d=h("stringStream").create(a),e=h("range");d.eatSpace();d.start=d.pos;for(var f;f=d.next();)if(/[\s\u00a0]/.test(f))c.push(e.create(d.start+b,d.pos-d.start-1)),d.eatSpace(),d.start=d.pos;c.push(e.create(d.start+b,d.pos- -d.start));return c}function d(a,c,d,f){a=b(a,h("xmlParser").parse(a),c);f&&a.reverse();return(c=e.find(a,function(a){return a.equal(d)}))?(f=e.indexOf(a,c),f1)?f[1]:e.find(a,function(a){return a.end>d.end})}function i(a,b){var c;if(a.charAt(b)=="<"&&(c=a.substring(b,a.length).match(q)))return h("range").create(b,c[0])}function j(a){return a=='"'||a=="'"}function l(a){var b= -a.valueRange(!0),c=[a.range(!0),b],d=h("stringStream"),f=h("cssEditTree"),g=h("range"),j=a.value();e.each(a.valueParts(),function(a){var h=a.clone();c.push(h.shift(b.start));var i=d.create(a.substring(j));if(i.match(/^[\w\-]+\(/,!0)){i.start=i.pos;i.skipToPair("(",")");var n=i.current();c.push(g.create(h.start+i.start,n));e.each(f.findParts(n),function(a){c.push(g.create(h.start+i.start+a.start,a.substring(n)))})}});return e.chain(c).filter(function(a){return!!a.length()}).uniq(!1,function(a){return a.toString()}).value()} -function n(a,b,c){var d=null,f=null,g=a.list(),h,j;c?(g.reverse(),h=function(a){return a.range(!0).start<=b.start},j=function(a){return a.start=b.end},j=function(a){return a.end>b.start});for(;d=e.find(g,h);){a=l(d);c&&a.reverse();if(f=e.find(a,function(a){return a.equal(b)})){if(f=e.indexOf(a,f),f!=a.length-1){f=a[f+1];break}}else{f=e.filter(a,function(a){return a.inside(b.end)});if(f.length>1){f=f[1];break}if(f=e.find(a,j))break}f=null;b.start=b.end= -c?d.range(!0).start-1:d.range(!0).end+1}return f}function m(a,b,c){a=h("cssEditTree").parse(a,{offset:b});b=a.nameRange(!0);return c.endb.start)?b:a}var q=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,r=h("actions");r.add("select_next_item",function(a){return a.getSyntax()=="css"?g(a,!1,h("cssEditTree").extractRule,m):f(a)});r.add("select_previous_item", -function(a){return a.getSyntax()=="css"?g(a,!0,h("cssEditTree").extractRule,o):c(a)})}); -emmet.exec(function(h){function e(c,b,a){var b=String((b||"out").toLowerCase()),d=h("editorUtils").outputInfo(c,a),a=d.syntax,e=h("range"),g=e.create(c.getSelectionRange()),d=d.content,l=null,l=f.last_match.opening_tag,n=f.last_match.closing_tag;if(b=="in"&&l&&g.length())if(n)l.start==g.start?d.charAt(l.end)=="<"?(b=e.create(f.find(d,l.end+1,a)),l=b.start==l.end&&b.end==n.start?e.create(f(d,l.end+1,a)):e.create(l.end,n.start-l.end)):l=e.create(l.end,n.start-l.end):(b=d.substring(0,n.start).indexOf("<", -l.end),l=e.create(f(d,b!=-1?b+1:l.end,a)));else return!1;else l=e.create(f(d,g.end,a));return l&&l.start!=-1?(c.createSelection(l.start,l.end),!0):!1}var g=h("actions"),f=h("html_matcher");g.add("match_pair",e,{hidden:!0});g.add("match_pair_inward",function(c){return e(c,"in")},{label:"HTML/Match Pair Tag (inward)"});g.add("match_pair_outward",function(c){return e(c,"out")},{label:"HTML/Match Pair Tag (outward)"});g.add("matching_pair",function(c){var b=String(c.getContent()),a=c.getCaretPos();b.charAt(a)== -"<"&&a++;var d=f.getTags(b,a,String(c.getProfileName()));if(d&&d[0]&&(b=d[0],d=d[1]))if(b.start<=a&&b.end>=a)return c.setCaretPos(d.start),!0;else if(d.start<=a&&d.end>=a)return c.setCaretPos(b.start),!0;return!1},{label:"HTML/Go To Matching Tag Pair"})}); -emmet.exec(function(h){h("actions").add("remove_tag",function(e){var g=h("utils"),f=h("editorUtils").outputInfo(e),c=h("html_matcher").getTags(f.content,e.getCaretPos(),f.profile);if(c&&c[0]){if(c[1]){var b=g.narrowToNonSpace(f.content,c[0].end,c[1].start-c[0].end),a=g.findNewlineBounds(f.content,b.start),a=g.getLinePadding(a.substring(f.content)),f=b.substring(f.content),f=g.unindentString(f,a);e.replaceContent(g.getCaretPlaceholder()+g.escapeText(f),c[0].start,c[1].end)}else e.replaceContent(g.getCaretPlaceholder(), -c[0].start,c[0].end);return!0}return!1},{label:"HTML/Remove Tag"})}); -emmet.exec(function(h){h("actions").add("split_join_tag",function(e,g){var f=h("html_matcher"),c=h("editorUtils").outputInfo(e,null,g),b=h("profile").get(c.profile);if((f=f.getTags(c.content,e.getCaretPos(),c.profile))&&f[0]){if(f[1]){c=h("utils");b=f[0].full_tag.replace(/\s*>$/,(b.self_closing_tag===!0?"/":" /")+">");if(b.length+f[0].start$/,">")+b+"";e.replaceContent(b,f[0].start,f[0].end)}f=!0}else f=!1;return f},{label:"HTML/Split\\Join Tag Declaration"})}); -emmet.define("reflectCSSValue",function(h,e){function g(b){var a=h("cssEditTree"),d=h("editorUtils").outputInfo(b),b=b.getCaretPos();if(a=a.parseFromPosition(d.content,b))if(d=a.itemFromPosition(b,!0)){var e=a.source,f=a.options.offset,b=b-f-d.range().start;c.exec(!1,[d]);if(e!==a.source)return{data:a.source,start:f,end:f+e.length,caret:f+d.range().start+b}}}function f(b){var b=h("cssEditTree").baseName(b),a;if(b=="opacity"||b=="filter")return/^(?:\-\w+\-)?(?:opacity|filter)$/;else if(a=b.match(/^border-radius-(top|bottom)(left|right)/))return RegExp("^(?:\\-\\w+\\-)?(?:"+ -b+"|border-"+a[1]+"-"+a[2]+"-radius)$");else if(a=b.match(/^border-(top|bottom)-(left|right)-radius/))return RegExp("^(?:\\-\\w+\\-)?(?:"+b+"|border-radius-"+a[1]+a[2]+")$");return RegExp("^(?:\\-\\w+\\-)?"+b+"$")}var c=h("handlerList").create();h("actions").add("reflect_css_value",function(b){return b.getSyntax()!="css"?!1:h("actionUtils").compoundUpdate(b,g(b))},{label:"CSS/Reflect Value"});c.add(function(b){var a=f(b.name());e.each(b.parent.list(),function(c){if(a.test(c.name())){var e;var f=b.name(), -g=b.value(),n=c.name();e=c.value();var m=h("cssEditTree"),o=h("utils"),f=m.baseName(f),n=m.baseName(n);e=f=="opacity"&&n=="filter"?e.replace(/opacity=[^)]*/i,"opacity="+Math.floor(parseFloat(g)*100)):f=="filter"&&n=="opacity"?(f=g.match(/opacity=([^)]*)/i))?o.prettifyNumber(parseInt(f[1])/100):e:g;c.value(e)}})},{order:-1});return{addHandler:function(b,a){c.add(b,a)},removeHandler:function(b){c.remove(b,options)}}}); -emmet.exec(function(h){h("actions").add("evaluate_math_expression",function(e){var g=h("actionUtils"),f=h("utils"),c=String(e.getContent()),b=h("range").create(e.getSelectionRange());b.length()||(b=g.findExpressionBounds(e,function(a){return f.isNumeric(a)||".+-*/\\".indexOf(a)!=-1}));if(b&&b.length()){g=b.substring(c);g=g.replace(/([\d\.\-]+)\\([\d\.\-]+)/g,"Math.round($1/$2)");try{var a=f.prettifyNumber((new Function("return "+g))());e.replaceContent(a,b.start,b.end);e.setCaretPos(b.start+a.length); -return!0}catch(d){}}return!1},{label:"Numbers/Evaluate Math Expression"})}); -emmet.exec(function(h,e){function g(b,a){var c=h("utils"),g=!1,j=!1,l=h("actionUtils").findExpressionBounds(b,function(a,b,e){return c.isNumeric(a)?!0:a=="."?!c.isNumeric(e.charAt(b+1))?!1:j?!1:j=!0:a=="-"?g?!1:g=!0:!1});if(l&&l.length()){var n=l.substring(String(b.getContent())),m=parseFloat(n);if(!e.isNaN(m)){m=c.prettifyNumber(m+a);if(/^(\-?)0+[1-9]/.test(n)){var o="";RegExp.$1&&(o="-",m=m.substring(1));m=m.split(".");m[0]=c.zeroPadString(m[0],f(n));m=o+m.join(".")}b.replaceContent(m,l.start,l.end); -b.createSelection(l.start,l.start+m.length);return!0}}return!1}function f(b){b=b.replace(/^\-/,"");return~b.indexOf(".")?b.split(".")[0].length:b.length}var c=h("actions");e.each([1,-1,10,-10,0.1,-0.1],function(b){var a=b>0?"increment":"decrement";c.add(a+"_number_by_"+String(Math.abs(b)).replace(".","").substring(0,2),function(a){return g(a,b)},{label:"Numbers/"+a.charAt(0).toUpperCase()+a.substring(1)+" number by "+Math.abs(b)})})}); -emmet.exec(function(h,e){var g=h("actions"),f=h("preferences");f.define("css.closeBraceIndentation","\n","Indentation before closing brace of CSS rule. Some users prefereindented closing brace of CSS rule for better readability. This preference\u2019s value will be automatically inserted before closing brace when user adds newline in newly created CSS rule (e.g. when \u201cInsert formatted linebreak\u201d action will be performed in CSS file). If you\u2019re such user, you may want to write put a value like \\n\\t in this preference."); -g.add("insert_formatted_line_break_only",function(c){var b=h("utils"),a=h("resources"),d=h("editorUtils").outputInfo(c),g=c.getCaretPos(),j=b.getNewline();if(e.include(["html","xml","xsl"],d.syntax)){if(a=a.getVariable("indentation"),d=h("html_matcher").getTags(d.content,g,d.profile),d[0]&&d[1]&&d[0].type=="tag"&&d[0].end==g&&d[1].start==g)return c.replaceContent(j+a+b.getCaretPlaceholder()+j,g),!0}else if(d.syntax=="css"&&(d=d.content,g&&d.charAt(g-1)=="{")){var l=f.get("css.closeBraceIndentation"), -a=a.getVariable("indentation"),n=d.charAt(g)=="}";if(!n)for(var m=g,o=d.length,q;ma.length?c.replaceContent(b+l,e,e,!0):c.replaceContent(b,e)}return!0},{hidden:!0})}); -emmet.exec(function(h){h("actions").add("merge_lines",function(e){var g=h("html_matcher"),f=h("utils"),c=h("editorUtils").outputInfo(e),b=h("range").create(e.getSelectionRange());if(!b.length()&&(g=g(c.content,e.getCaretPos(),c.profile)))b.start=g[0],b.end=g[1];if(b.length()){c=b.substring(c.content);f=f.splitByLines(c);for(c=1;c=0;)if(e("src=",b,c)){if(b=b.substr(c).match(/^(src=(["'])?)([^'"<>\s]+)\1?/))f=b[3],c+=b[1].length;break}else if(e("url(",b,c)){if(b=b.substr(c).match(/^(url\((['"])?)([^'"\)\s]+)\1?/))f=b[3],c+=b[1].length;break}if(f)if(e("data:",f))if(b=String(g.prompt("Enter path to file (absolute or relative)"))){var a= -h("file"),d=a.createPath(g.getFilePath(),b);if(!d)throw"Can't save file";a.save(d,h("base64").decode(f.replace(/^data\:.+?;.+?,/,"")));g.replaceContent("$0"+b,c,c+f.length);g=!0}else g=!1;else{b=h("file");a=h("actionUtils");d=g.getFilePath();if(d===null)throw"You should save your file before using this action";d=b.locateFile(d,f);if(d===null)throw"Can't find "+f+" file";var i=h("base64").encode(String(b.read(d)));if(!i)throw"Can't encode file content to base64";i="data:"+(a.mimeTypes[String(b.getExt(d))]|| -"application/octet-stream")+";base64,"+i;g.replaceContent("$0"+i,c,c+f.length);g=!0}else g=!1;return g},{label:"Encode\\Decode data:URL image"})}); -emmet.exec(function(h,e){function g(e,c){var b;if(c){if(/^data:/.test(c))b=h("base64").decode(c.replace(/^data\:.+?;.+?,/,""));else{b=h("file");var a=b.locateFile(e.getFilePath(),c);if(a===null)throw"Can't find "+c+" file";b=String(b.read(a))}return h("actionUtils").getImageSize(b)}}h("actions").add("update_image_size",function(f){var c;if(String(f.getSyntax())=="css")a:{c=f.getCaretPos();var b=h("editorUtils").outputInfo(f);if(b=h("cssEditTree").parseFromPosition(b.content,c,!0)){var a=b.itemFromPosition(c, -!0),d;if(a&&(d=/url\((["']?)(.+?)\1\)/i.exec(a.value()||"")))if(d=g(f,d[2])){a=b.range(!0);b.value("width",d.width+"px");b.value("height",d.height+"px",b.indexOf("width")+1);c=e.extend(a,{data:b.toString(),caret:c});break a}}c=null}else a:{c=f.getCaretPos();b=h("editorUtils").outputInfo(f);if((b=h("xmlEditTree").parseFromPosition(b.content,c,!0))&&b.name().toLowerCase()=="img")if(d=g(f,b.value("src"))){a=b.range(!0);b.value("width",d.width);b.value("height",d.height,b.indexOf("width")+1);c=e.extend(a, -{data:b.toString(),caret:c});break a}c=null}return h("actionUtils").compoundUpdate(f,c)})}); -emmet.define("cssResolver",function(h,e){function g(a){var b=a&&a.charCodeAt(0);return a&&a=="."||b>47&&b<58}function f(a){a=h("utils").trim(a);if(~a.indexOf("/*"))return!1;a=h("tabStops").processText(a,{replaceCarets:!0,tabstop:function(){return"value"}});return a.split(":").length==2}function c(a){var b=h("utils"),a=b.trim(a);if(a.indexOf(":")==-1)return{name:a,value:o};a=a.split(":");return{name:b.trim(a.shift()),value:b.trim(a.join(":")).replace(/^(\$\{0\}|\$0)(\s*;?)$/,"${1}$2")}}function b(a, -b){var c=n[b];c||(c=e.find(n,function(a){return a.prefix==b}));return c&&c.supports&&e.include(c.supports,a)}function a(a,c){var d=[];e.each(n,function(c,e){b(a,e)&&d.push(e)});!d.length&&!c&&e.each(n,function(a,b){a.obsolete||d.push(b)});return d}function d(a,b){e.isString(b)&&(b={prefix:b});n[a]=e.extend({},l,b)}function i(a,b){if(!e.isString(a))a=a.data;if(!f(a))return a;b&&(~a.indexOf(";")?a=a.split(";").join(" !important;"):a+=" !important");var c=a.indexOf(":");return a=a.substring(0,c).replace(/\s+$/, -"")+q.get("css.valueSeparator")+h("utils").trim(a.substring(c+1))}var j=null,l={prefix:"emmet",obsolete:!1,transformName:function(a){return"-"+this.prefix+"-"+a},supports:null},n={},m={p:"%",e:"em",x:"ex"},o="${1};",q=h("preferences");q.define("css.valueSeparator",": ","Defines a symbol that should be placed between CSS property and value when expanding CSS abbreviations.");q.define("css.autoInsertVendorPrefixes",!0,"Automatically generate vendor-prefixed copies of expanded CSS property. By default, Emmet will generate vendor-prefixed NaN(e.g. -bxsh). With this option enabled, you don\u2019t need dashes before abbreviations: Emmet will produce vendor-prefixed properties for you."); -var r=e.template("A comma-separated list of CSS properties that may have <%= vendor %> vendor prefix. This list is used to generate a list of prefixed properties when expanding -property abbreviations. Empty list means that all possible CSS values may have <%= vendor %> prefix.");e.each({webkit:"animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow", -moz:"animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow", -ms:"accelerator, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, overflow-x, overflow-y, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, word-wrap, wrap-flow, wrap-margin, wrap-through, writing-mode, zoom", +var _=function(){function h(a,b,c){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a._chain)a=a._wrapped;if(b._chain)b=b._wrapped;if(a.isEqual&&k.isFunction(a.isEqual))return a.isEqual(b);if(b.isEqual&&k.isFunction(b.isEqual))return b.isEqual(a);var e=g.call(a);if(e!=g.call(b))return!1;switch(e){case "[object String]":return a==String(b);case "[object Number]":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case "[object Date]":case "[object Boolean]":return+a==+b;case "[object RegExp]":return a.source== +b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return!1;for(var d=c.length;d--;)if(c[d]==a)return!0;c.push(a);var d=0,j=!0;if(e=="[object Array]"){if(d=a.length,j=d==b.length)for(;d--;)if(!(j=d in a==d in b&&h(a[d],b[d],c)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var f in a)if(k.has(a,f)&&(d++,!(j=k.has(b,f)&&h(a[f],b[f],c))))break;if(j){for(f in b)if(k.has(b,f)&&!d--)break; +j=!d}}c.pop();return j}var d=this,f=d._,i={},b=Array.prototype,c=Object.prototype,a=b.slice,e=b.unshift,g=c.toString,j=c.hasOwnProperty,l=b.forEach,m=b.map,n=b.reduce,o=b.reduceRight,q=b.filter,s=b.every,r=b.some,u=b.indexOf,p=b.lastIndexOf,c=Array.isArray,v=Object.keys,w=Function.prototype.bind,k=function(a){return new y(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=k;exports._=k}else d._=k;k.VERSION="1.3.3";var t=k.each=k.forEach=function(a, +b,c){if(a!=null)if(l&&a.forEach===l)a.forEach(b,c);else if(a.length===+a.length)for(var g=0,e=a.length;g2;a==null&&(a=[]);if(n&& +a.reduce===n)return g&&(b=k.bind(b,g)),e?a.reduce(b,c):a.reduce(b);t(a,function(a,d,j){e?c=b.call(g,c,a,d,j):(c=a,e=!0)});if(!e)throw new TypeError("Reduce of empty array with no initial value");return c};k.reduceRight=k.foldr=function(a,b,c,g){var e=arguments.length>2;a==null&&(a=[]);if(o&&a.reduceRight===o)return g&&(b=k.bind(b,g)),e?a.reduceRight(b,c):a.reduceRight(b);var d=k.toArray(a).reverse();g&&!e&&(b=k.bind(b,g));return e?k.reduce(d,b,c,g):k.reduce(d,b)};k.find=k.detect=function(a,b,c){var g; +z(a,function(a,e,d){if(b.call(c,a,e,d))return g=a,!0});return g};k.filter=k.select=function(a,b,c){var g=[];if(a==null)return g;if(q&&a.filter===q)return a.filter(b,c);t(a,function(a,e,d){b.call(c,a,e,d)&&(g[g.length]=a)});return g};k.reject=function(a,b,c){var g=[];if(a==null)return g;t(a,function(a,e,d){b.call(c,a,e,d)||(g[g.length]=a)});return g};k.every=k.all=function(a,b,c){var g=!0;if(a==null)return g;if(s&&a.every===s)return a.every(b,c);t(a,function(a,e,d){if(!(g=g&&b.call(c,a,e,d)))return i}); +return!!g};var z=k.some=k.any=function(a,b,c){b||(b=k.identity);var g=!1;if(a==null)return g;if(r&&a.some===r)return a.some(b,c);t(a,function(a,e,d){if(g||(g=b.call(c,a,e,d)))return i});return!!g};k.include=k.contains=function(a,b){var c=!1;return a==null?c:u&&a.indexOf===u?a.indexOf(b)!=-1:c=z(a,function(a){return a===b})};k.invoke=function(b,c){var g=a.call(arguments,2);return k.map(b,function(a){return(k.isFunction(c)?c||a:a[c]).apply(a,g)})};k.pluck=function(a,b){return k.map(a,function(a){return a[b]})}; +k.max=function(a,b,c){if(!b&&k.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!b&&k.isEmpty(a))return-Infinity;var g={computed:-Infinity};t(a,function(a,e,d){e=b?b.call(c,a,e,d):a;e>=g.computed&&(g={value:a,computed:e})});return g.value};k.min=function(a,b,c){if(!b&&k.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!b&&k.isEmpty(a))return Infinity;var g={computed:Infinity};t(a,function(a,e,d){e=b?b.call(c,a,e,d):a;eg?1:0}),"value")};k.groupBy=function(a,b){var c={},g=k.isFunction(b)?b:function(a){return a[b]};t(a,function(a,b){var e=g(a,b);(c[e]||(c[e]=[])).push(a)}); +return c};k.sortedIndex=function(a,b,c){c||(c=k.identity);for(var g=0,e=a.length;g>1;c(a[d])=0})})};k.difference=function(b){var c=k.flatten(a.call(arguments,1),!0);return k.filter(b,function(a){return!k.include(c,a)})};k.zip=function(){for(var b=a.call(arguments),c=k.max(k.pluck(b,"length")), +g=Array(c),e=0;e=0;c--)b=[a[c].apply(this,b)];return b[0]}};k.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};k.keys=v||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],c;for(c in a)k.has(a,c)&&(b[b.length]=c);return b};k.values=function(a){return k.map(a,k.identity)};k.functions=k.methods=function(a){var b=[],c;for(c in a)k.isFunction(a[c])&&b.push(c);return b.sort()};k.extend=function(b){t(a.call(arguments, +1),function(a){for(var c in a)b[c]=a[c]});return b};k.pick=function(b){var c={};t(k.flatten(a.call(arguments,1)),function(a){a in b&&(c[a]=b[a])});return c};k.defaults=function(b){t(a.call(arguments,1),function(a){for(var c in a)b[c]==null&&(b[c]=a[c])});return b};k.clone=function(a){return!k.isObject(a)?a:k.isArray(a)?a.slice():k.extend({},a)};k.tap=function(a,b){b(a);return a};k.isEqual=function(a,b){return h(a,b,[])};k.isEmpty=function(a){if(a==null)return!0;if(k.isArray(a)||k.isString(a))return a.length=== +0;for(var b in a)if(k.has(a,b))return!1;return!0};k.isElement=function(a){return!!(a&&a.nodeType==1)};k.isArray=c||function(a){return g.call(a)=="[object Array]"};k.isObject=function(a){return a===Object(a)};k.isArguments=function(a){return g.call(a)=="[object Arguments]"};if(!k.isArguments(arguments))k.isArguments=function(a){return!(!a||!k.has(a,"callee"))};k.isFunction=function(a){return g.call(a)=="[object Function]"};k.isString=function(a){return g.call(a)=="[object String]"};k.isNumber=function(a){return g.call(a)== +"[object Number]"};k.isFinite=function(a){return k.isNumber(a)&&isFinite(a)};k.isNaN=function(a){return a!==a};k.isBoolean=function(a){return a===!0||a===!1||g.call(a)=="[object Boolean]"};k.isDate=function(a){return g.call(a)=="[object Date]"};k.isRegExp=function(a){return g.call(a)=="[object RegExp]"};k.isNull=function(a){return a===null};k.isUndefined=function(a){return a===void 0};k.has=function(a,b){return j.call(a,b)};k.noConflict=function(){d._=f;return this};k.identity=function(a){return a}; +k.times=function(a,b,c){for(var g=0;g/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};k.result=function(a,b){if(a==null)return null;var c=a[b];return k.isFunction(c)?c.call(a):c};k.mixin=function(a){t(k.functions(a),function(b){F(b,k[b]=a[b])})};var G=0;k.uniqueId=function(a){var b=G++;return a?a+b:b};k.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g, +escape:/<%-([\s\S]+?)%>/g};var B=/.^/,A={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},C;for(C in A)A[A[C]]=C;var H=/\\|'|\r|\n|\t|\u2028|\u2029/g,I=/\\(\\|'|r|n|t|u2028|u2029)/g,D=function(a){return a.replace(I,function(a,b){return A[b]})};k.template=function(a,b,c){c=k.defaults(c||{},k.templateSettings);a="__p+='"+a.replace(H,function(a){return"\\"+A[a]}).replace(c.escape||B,function(a,b){return"'+\n_.escape("+D(b)+")+\n'"}).replace(c.interpolate||B,function(a,b){return"'+\n("+ +D(b)+")+\n'"}).replace(c.evaluate||B,function(a,b){return"';\n"+D(b)+"\n;__p+='"})+"';\n";c.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",g=new Function(c.variable||"obj","_",a);if(b)return g(b,k);b=function(a){return g.call(this,a,k)};b.source="function("+(c.variable||"obj")+"){\n"+a+"}";return b};k.chain=function(a){return k(a).chain()};var y=function(a){this._wrapped=a};k.prototype=y.prototype; +var E=function(a,b){return b?k(a).chain():a},F=function(b,c){y.prototype[b]=function(){var b=a.call(arguments);e.call(b,this._wrapped);return E(c.apply(k,b),this._chain)}};k.mixin(k);t(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=b[a];y.prototype[a]=function(){var b=this._wrapped;c.apply(b,arguments);var g=b.length;(a=="shift"||a=="splice")&&g===0&&delete b[0];return E(b,this._chain)}});t(["concat","join","slice"],function(a){var c=b[a];y.prototype[a]=function(){return E(c.apply(this._wrapped, +arguments),this._chain)}});y.prototype.chain=function(){this._chain=!0;return this};y.prototype.value=function(){return this._wrapped};return k}.call({}),emmet=function(h){function d(a,b,d){var f;f=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};_.extend(f,a);c.prototype=a.prototype;f.prototype=new c;b&&_.extend(f.prototype,b);d&&_.extend(f,d);f.prototype.constructor=f;f.__super__=a.prototype;return f}function f(c){!(c in b)&&a&&a(c);return b[c]}if(typeof _=="undefined")try{_= +h.require("underscore")}catch(i){}if(typeof _=="undefined")throw"Cannot access to Underscore.js lib";var b={_:_},c=function(){},a=null;return{define:function(a,c){a in b||(b[a]=_.isFunction(c)?this.exec(c):c)},require:f,exec:function(a,b){return a.call(b||h,_.bind(f,this),_,this)},extend:function(a,b){var c=d(this,a,b);c.extend=this.extend;if(a.hasOwnProperty("toString"))c.prototype.toString=a.toString;return c},expandAbbreviation:function(a,b,c,d){if(!a)return"";var b=b||"html",h=f("filters"),i= +f("abbreviationParser"),c=f("profile").get(c,b);f("tabStops").resetTabstopIndex();a=h.extractFromAbbreviation(a);d=i.parse(a[0],{syntax:b,contextNode:d});b=h.composeList(b,c,a[1]);h.apply(d,b,c);return d.toString()},defaultSyntax:function(){return"html"},defaultProfile:function(){return"plain"},log:function(){h.console&&h.console.log&&h.console.log.apply(h.console,arguments)},setModuleLoader:function(b){a=b}}}(this); +if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=emmet;exports.emmet=emmet} +emmet.define("abbreviationParser",function(h,d){function f(){this.parent=null;this.children=[];this._attributes=[];this.abbreviation="";this.counter=1;this._name=null;this._text="";this.repeatCount=1;this.hasImplicitRepeat=!1;this._data={};this.padding=this.content=this.end=this.start=""}function i(a){return a.substring(1,a.length-1)}function b(a){for(var a=h("utils").trim(a),c=new f,g=c.addChild(),e,j=h("stringStream").create(a),a=1E3,l;!j.eol()&&--a>0;)switch(e=j.peek(),e){case "(":j.start=j.pos; +if(j.skipToPair("(",")"))e=b(i(j.current())),(l=j.match(/^\*(\d+)?/,!0))&&g._setRepeat(l[1]),d.each(e.children,function(a){g.addChild(a)});else throw'Invalid abbreviation: mo matching ")" found for character at '+j.pos;break;case ">":g=g.addChild();j.next();break;case "+":g=g.parent.addChild();j.next();break;case "^":e=g.parent||g;g=(e.parent||e).addChild();j.next();break;default:j.start=j.pos,j.eatWhile(function(a){if(a=="["||a=="{"){if(j.skipToPair(a,q[a]))return j.backUp(1),!0;throw'Invalid abbreviation: mo matching "'+ +q[a]+'" found for character at '+j.pos;}return a=="+"?(j.next(),a=j.eol()||~"+>^*".indexOf(j.peek()),j.backUp(1),a):a!="("&&m(a)}),g.setAbbreviation(j.current()),j.start=j.pos}if(a<1)throw"Endless loop detected";return c}function c(a){var a=h("utils").trim(a),b=[],a=h("stringStream").create(a);for(a.eatSpace();!a.eol();)if(a.start=a.pos,a.eatWhile(o)){var c=a.current(),g="";if(a.peek()=="="){a.next();a.start=a.pos;var e=a.peek();if(e=='"'||e=="'"){a.next();a:{for(var g=a,d=void 0;d=g.next();)if(d=== +e){g=!0;break a}g=!1}if(g)g=a.current(),g=g.substring(1,g.length-1);else throw"Invalid attribute value";}else if(a.eatWhile(/[^\s\]]/))g=a.current();else throw"Invalid attribute value";}b.push({name:c,value:g});a.eatSpace()}else break;return b}function a(a){for(var b=[],g={"#":"id",".":"class"},d=null,j=h("stringStream").create(a);!j.eol();)switch(j.peek()){case "#":case ".":if(d===null)d=j.pos;var f=g[j.peek()];j.next();j.start=j.pos;j.eatWhile(o);b.push({name:f,value:j.current()});break;case "[":if(d=== +null)d=j.pos;j.start=j.pos;if(!j.skipToPair("[","]"))throw"Invalid attribute set definition";b=b.concat(c(i(j.current())));break;default:j.next()}return!b.length?null:{element:a.substring(0,d),attributes:e(b)}}function e(a){var a=d.map(a,function(a){return d.clone(a)}),b={};return d.filter(a,function(a){if(!(a.name in b))return b[a.name]=a;var c=b[a.name];a.name.toLowerCase()=="class"?c.value+=(c.value.length?" ":"")+a.value:c.value=a.value;return!1})}function g(a){if(!~a.indexOf("{"))return null; +for(var b=h("stringStream").create(a);!b.eol();)switch(b.peek()){case "[":case "(":b.skipToPair(b.peek(),q[b.peek()]);break;case "{":return b.start=b.pos,b.skipToPair("{","}"),{element:a.substring(0,b.start),text:i(b.current())};default:b.next()}}function j(a){for(var b=a.children.length-1,c,g;b>=0;b--)if(g=a.children[b],g.isRepeating()){c=g.repeatCount;g.repeatCount=1;for(g.updateProperty("counter",1);--c>0;)g.parent.addChild(g.clone(),b+1).updateProperty("counter",c+1)}d.each(a.children,j);return a} +function l(a){for(var b=a.children.length-1;b>=0;b--){var c=a.children[b];c.isGroup()?c.replace(l(c).children):c.isEmpty()&&c.remove()}d.each(a.children,l);return a}function m(a){var b=a.charCodeAt(0);return b>64&&b<91||b>96&&b<123||b>47&&b<58||"#.*:$-_!@|%".indexOf(a)!=-1}var n=/^[\w\-\$\:@\!%]+\+?$/i,o=/[\w\-:\$]/,q={"[":"]","(":")","{":"}"},s=Array.prototype.splice,r=[],u=[],p=[];f.prototype={addChild:function(a,b){a=a||new f;a.parent=this;d.isUndefined(b)?this.children.push(a):this.children.splice(b, +0,a);return a},clone:function(){var a=new f;d.each(["abbreviation","counter","_name","_text","repeatCount","hasImplicitRepeat","start","end","content","padding"],function(b){a[b]=this[b]},this);a._attributes=d.map(this._attributes,function(a){return d.clone(a)});a._data=d.clone(this._data);a.children=d.map(this.children,function(b){b=b.clone();b.parent=a;return b});return a},remove:function(){if(this.parent)this.parent.children=d.without(this.parent.children,this);return this},replace:function(){var a= +this.parent,b=d.indexOf(a.children,this),c=d.flatten(arguments);s.apply(a.children,[b,1].concat(c));d.each(c,function(b){b.parent=a})},updateProperty:function(a,b){this[a]=b;d.each(this.children,function(c){c.updateProperty(a,b)})},find:function(a){return this.findAll(a)[0]},findAll:function(a){if(!d.isFunction(a))var b=a.toLowerCase(),a=function(a){return a.name().toLowerCase()==b};var c=[];d.each(this.children,function(b){a(b)&&c.push(b);c=c.concat(b.findAll(a))});return d.compact(c)},data:function(a, +b){if(arguments.length==2&&(this._data[a]=b,a=="resource"&&h("elements").is(b,"snippet")&&(this.content=b.data,this._text)))this.content=h("abbreviationUtils").insertChildContent(b.data,this._text);return this._data[a]},name:function(){var a=this.matchedResource();return h("elements").is(a,"element")?a.name:this._name},attributeList:function(){var a=[],b=this.matchedResource();h("elements").is(b,"element")&&d.isArray(b.attributes)&&(a=a.concat(b.attributes));return e(a.concat(this._attributes))}, +attribute:function(a,b){if(arguments.length==2){var c=d.indexOf(d.pluck(this._attributes,"name"),a.toLowerCase());~c?this._attributes[c].value=b:this._attributes.push({name:a,value:b})}return(d.find(this.attributeList(),function(b){return b.name==a})||{}).value},matchedResource:function(){return this.data("resource")},index:function(){return this.parent?d.indexOf(this.parent.children,this):-1},_setRepeat:function(a){a?this.repeatCount=parseInt(a,10)||1:this.hasImplicitRepeat=!0},setAbbreviation:function(b){var c= +this;this.abbreviation=b=(b||"").replace(/\*(\d+)?$/,function(a,b){c._setRepeat(b);return""});var e=g(b);if(e)b=e.element,this.content=this._text=e.text;if(e=a(b))b=e.element,this._attributes=e.attributes;if((this._name=b)&&!n.test(this._name))throw"Invalid abbreviation";},toString:function(){var a=h("utils"),b=this.start,c=this.end,g=this.content,e=this;d.each(p,function(a){b=a(b,e,"start");g=a(g,e,"content");c=a(c,e,"end")});var j=d.map(this.children,function(a){return a.toString()}).join(""),g= +h("abbreviationUtils").insertChildContent(g,j,{keepVariable:!1});return b+a.padString(g,this.padding)+c},hasEmptyChildren:function(){return!!d.find(this.children,function(a){return a.isEmpty()})},hasImplicitName:function(){return!this._name&&!this.isTextNode()},isGroup:function(){return!this.abbreviation},isEmpty:function(){return!this.abbreviation&&!this.children.length},isRepeating:function(){return this.repeatCount>1||this.hasImplicitRepeat},isTextNode:function(){return!this.name()&&!this.attributeList().length}, +isElement:function(){return!this.isEmpty()&&!this.isTextNode()},deepestChild:function(){if(!this.children.length)return null;for(var a=this;a.children.length;)a=d.last(a.children);return a}};p.push(function(a,b){return h("utils").replaceCounter(a,b.counter)});return{parse:function(a,c){var c=c||{},g=b(a);if(c.contextNode){g._name=c.contextNode.name;var e={};d.each(g._attributes,function(a){e[a.name]=a});d.each(c.contextNode.attributes,function(a){a.name in e?e[a.name].value=a.value:(a=d.clone(a), +g._attributes.push(a),e[a.name]=a)})}d.each(r,function(a){a(g,c)});g=l(j(g));d.each(u,function(a){a(g,c)});return g},AbbreviationNode:f,addPreprocessor:function(a){d.include(r,a)||r.push(a)},removeFilter:function(a){preprocessor=d.without(r,a)},addPostprocessor:function(a){d.include(u,a)||u.push(a)},removePostprocessor:function(a){u=d.without(u,a)},addOutputProcessor:function(a){d.include(p,a)||p.push(a)},removeOutputProcessor:function(a){p=d.without(p,a)},isAllowedChar:function(a){a=String(a);return m(a)|| +~">+^[](){}".indexOf(a)}}}); +emmet.exec(function(h,d){function f(i,b){var c=h("resources"),a=h("elements"),e=h("abbreviationParser");d.each(d.clone(i.children),function(g){var j=c.getMatchedResource(g,b);if(d.isString(j))g.data("resource",a.create("snippet",j));else if(a.is(j,"reference")){j=e.parse(j.data,{syntax:b});if(g.repeatCount>1){var h=j.findAll(function(a){return a.hasImplicitRepeat});d.each(h,function(a){a.repeatCount=g.repeatCount;a.hasImplicitRepeat=!1})}var i=j.deepestChild();i&&d.each(g.children,function(a){i.addChild(a)}); +d.each(j.children,function(a){d.each(g.attributeList(),function(b){a.attribute(b.name,b.value)})});g.replace(j.children)}else g.data("resource",j);f(g,b)})}h("abbreviationParser").addPreprocessor(function(d,b){var c=b.syntax||emmet.defaultSyntax();f(d,c)})}); +emmet.exec(function(h,d){function f(a){for(var b=h("range"),c=[],a=h("stringStream").create(a);!a.eol();){if(a.peek()=="\\")a.next();else if(a.start=a.pos,a.match(e,!0)){c.push(b.create(a.start,e));continue}a.next()}return c}function i(a,b){var c=h("utils"),e=f(a);e.reverse();d.each(e,function(e){a=c.replaceSubstring(a,b,e)});return a}function b(a){return f(a.content).length?!0:!!d.find(a.attributeList(),function(a){return!!f(a.value).length})}function c(a,c,e){var f=a.findAll(function(a){return b(a)}); +b(a)&&f.unshift(a);f.length?d.each(f,function(a){a.content=i(a.content,c);d.each(a._attributes,function(a){a.value=i(a.value,c)})}):(a=a.deepestChild()||a,a.content=e?c:h("abbreviationUtils").insertChildContent(a.content,c))}var a=h("abbreviationParser"),e="$#";a.addPreprocessor(function(a,b){if(b.pastedContent){var c=h("utils"),e=d.map(c.splitByLines(b.pastedContent,!0),c.trim);a.findAll(function(a){if(a.hasImplicitRepeat)return a.data("paste",e),a.repeatCount=e.length})}});a.addPostprocessor(function(a, +b){!a.findAll(function(a){var b=a.data("paste"),g="";d.isArray(b)?g=b[a.counter-1]:d.isFunction(b)?g=b(a.counter-1,a.content):b&&(g=b);g&&c(a,g,!!a.data("pasteOverwrites"));a.data("paste",null);return!!b}).length&&b.pastedContent&&c(a,b.pastedContent)})});emmet.exec(function(h,d){function f(i){var b=h("tagName");d.each(i.children,function(c){if(c.hasImplicitName()||c.data("forceNameResolving"))c._name=b.resolve(c.parent.name());f(c)});return i}h("abbreviationParser").addPostprocessor(f)}); +emmet.define("cssParser",function(h,d){function f(a){return typeof a!=="undefined"}function i(){return{"char":g.chnum,line:g.linenum}}function b(a,b,c){var e=g,c=c||{};j.push({charstart:f(c["char"])?c["char"]:e.chnum,charend:f(c.charend)?c.charend:e.chnum,linestart:f(c.line)?c.line:e.linenum,lineend:f(c.lineend)?c.lineend:e.linenum,value:a,type:b||a})}function c(a,b){var c=g,e=b||{},d=f(e["char"])?e["char"]:c.chnum,e=f(e.line)?e.line:c.linenum;return{name:"ParseError",message:a+" at line "+(e+1)+ +" char "+(d+1),walker:c,tokens:j}}function a(a){var c=g,e=c.ch,d=i(),j=a?a+e:e,e=c.nextChar();for(a&&(d["char"]-=a.length);m(e)||n(e);)j+=e,e=c.nextChar();b(j,"identifier",d)}function e(){var e=g.ch;if(e===" "||e==="\t"){for(var d=g.ch,j="",f=i();d===" "||d==="\t";)j+=d,d=g.nextChar();b(j,"white",f)}else{if(e==="/"){var d=g,e=f=d.ch,h,p=i();h=d.nextChar();if(h!=="*")p.charend=p["char"],p.lineend=p.line,j=b(e,e,p);else{for(;!(f==="*"&&h==="/");)e+=h,f=h,h=d.nextChar();e+=h;d.nextChar();b(e,"comment", +p)}return j}if(e==='"'||e==="'"){d=g;e=f=j=d.ch;p=i();for(j=d.nextChar();j!==f;){if(j==="\n")if(h=d.nextChar(),h==="\\")e+=j+h;else throw c("Unterminated string",p);else e+=j==="\\"?j+d.nextChar():j;j=d.nextChar()}e+=j;d.nextChar();b(e,"string",p)}else if(e==="("){d=g;j=d.ch;f=0;e=j;h=i();for(j=d.nextChar();j!==")"&&!f;){if(j==="(")f++;else if(j===")")f--;else if(j===!1)throw c("Unterminated brace",h);e+=j;j=d.nextChar()}e+=j;d.nextChar();b(e,"brace",h)}else{if(e==="-"||e==="."||n(e)){j=g;f=j.ch; +e=i();h=f;var p=h===".",v,f=j.nextChar();v=!n(f);if(p&&v)e.charend=e["char"],e.lineend=e.line,d=b(h,".",e);else if(h==="-"&&v)d=a("-");else{for(;f!==!1&&(n(f)||!p&&f===".");)f==="."&&(p=!0),h+=f,f=j.nextChar();b(h,"number",e)}return d}if(m(e))return a();if(l(e))return d=g,e=d.ch,j=i(),h=d.nextChar(),h==="="&&l(e,!0)?(e+=h,b(e,"match",j),d.nextChar(),f=void 0):(j.charend=j["char"]+1,j.lineend=j.line,b(e,e,j)),f;if(e==="\n")b("line"),g.nextChar();else throw c("Unrecognized character");}}}var g,j=[], +l,m,n;g={lines:null,total_lines:0,linenum:-1,line:"",ch:"",chnum:-1,init:function(a){var b=g;b.lines=a.replace(/\r\n/g,"\n").replace(/\r/g,"\n").split("\n");b.total_lines=b.lines.length;b.chnum=-1;b.linenum=-1;b.ch="";b.line="";b.nextLine();b.nextChar()},nextLine:function(){this.linenum+=1;this.line=this.total_lines<=this.linenum?!1:this.lines[this.linenum];if(this.chnum!==-1)this.chnum=0;return this.line},nextChar:function(){for(this.chnum+=1;this.line.charAt(this.chnum)==="";){if(this.nextLine()=== +!1)return this.ch=!1;this.chnum=-1;return this.ch="\n"}return this.ch=this.line.charAt(this.chnum)},peek:function(){return this.line.charAt(this.chnum+1)}};m=function(a){return a=="&"||a==="_"||a==="-"||a>="a"&&a<="z"||a>="A"&&a<="Z"};n=function(a){return a!==!1&&a>="0"&&a<="9"};l=function(){for(var a="{}[]()+*=.,;:>~|\\%$#@^!".split(""),b="*^|$~".split(""),c={},e={},g=0;g")):null:a.match("--")?g(b("comment","--\>")):a.match("DOCTYPE",!0,!0)?(a.eatWhile(/[\w\._\-]/),g(c(1))):null;else if(a.eat("?"))return a.eatWhile(/[\w\._\-]/),e.tokenize=b("meta","?>"),"meta";else{w=a.eat("/")?"closeTag":"openTag";a.eatSpace();for(v="";d=a.eat(/[^\s\u00a0=<>\"\'\/?]/);)v+=d;e.tokenize=f;return"tag"}else return d== +"&"?(a.eat("#")?a.eat("x")?a.eatWhile(/[a-fA-F\d]/)&&a.eat(";"):a.eatWhile(/[\d]/)&&a.eat(";"):a.eatWhile(/[\w\.\-:]/)&&a.eat(";"))?"atom":"error":(a.eatWhile(/[^&<]/),"text")}function f(a,b){var c=a.next();return c==">"||c=="/"&&a.eat(">")?(b.tokenize=d,w=c==">"?"endTag":"selfcloseTag","tag"):c=="="?(w="equals",null):/[\'\"]/.test(c)?(b.tokenize=i(c),b.tokenize(a,b)):(a.eatWhile(/[^\s\u00a0=<>\"\'\/?]/),"word")}function i(a){return function(b,c){for(;!b.eol();)if(b.next()==a){c.tokenize=f;break}return"string"}} +function b(a,b){return function(c,e){for(;!c.eol();){if(c.match(b)){e.tokenize=d;break}c.next()}return a}}function c(a){return function(b,e){for(var g;(g=b.next())!=null;)if(g=="<")return e.tokenize=c(a+1),e.tokenize(b,e);else if(g==">")if(a==1){e.tokenize=d;break}else return e.tokenize=c(a-1),e.tokenize(b,e);return"meta"}}function a(){for(var a=arguments.length-1;a>=0;a--)k.cc.push(arguments[a])}function e(){a.apply(null,arguments);return!0}function g(){if(k.context)k.context=k.context.prev}function j(a){if(a== +"openTag")return k.tagName=v,e(o,l(k.startOfLine));else if(a=="closeTag")return a=!1,k.context?k.context.tagName!=v&&(p.implicitlyClosed.hasOwnProperty(k.context.tagName.toLowerCase())&&g(),a=!k.context||k.context.tagName!=v):a=!0,a&&(t="error"),e(m(a));return e()}function l(a){return function(b){if(b=="selfcloseTag"||b=="endTag"&&p.autoSelfClosers.hasOwnProperty(k.tagName.toLowerCase()))return n(k.tagName.toLowerCase()),e();if(b=="endTag"){n(k.tagName.toLowerCase());var b=k.tagName,c=p.doNotIndent.hasOwnProperty(b)|| +k.context&&k.context.noIndent;k.context={prev:k.context,tagName:b,indent:k.indented,startOfLine:a,noIndent:c}}return e()}}function m(a){return function(b){a&&(t="error");if(b=="endTag")return g(),e();t="error";return e(arguments.callee)}}function n(a){for(var b;;){if(!k.context)break;b=k.context.tagName.toLowerCase();if(!p.contextGrabbers.hasOwnProperty(b)||!p.contextGrabbers[b].hasOwnProperty(a))break;g()}}function o(b){if(b=="word")return t="attribute",e(q,o);if(b=="endTag"||b=="selfcloseTag")return a(); +t="error";return e(o)}function q(b){if(b=="equals")return e(s,o);p.allowMissing||(t="error");return b=="endTag"||b=="selfcloseTag"?a():e()}function s(b){if(b=="string")return e(r);if(b=="word"&&p.allowUnquoted)return t="string",e();t="error";return b=="endTag"||b=="selfCloseTag"?a():e()}function r(b){return b=="string"?e(r):a()}function u(a,b){if(a.sol())b.startOfLine=!0,b.indented=0;if(a.eatSpace())return null;t=w=v=null;var c=b.tokenize(a,b);b.type=w;if((c||w)&&c!="comment")for(k=b;;)if((b.cc.pop()|| +j)(w||c))break;b.startOfLine=!1;return t||c}var p={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!0,allowMissing:!0},v=null,w=null,k=null,t;return{parse:function(a,b){for(var b=b||0,c={tokenize:d,cc:[],indented:0,startOfLine:!0,tagName:null,context:null},e=h("stringStream").create(a),g=[];!e.eol();)g.push({type:u(e,c),start:e.start+b,end:e.pos+b}),e.start=e.pos;return g}}}); +emmet.define("string-score",function(){return{score:function(h,d,f){if(h==d)return 1;if(d=="")return 0;for(var i=0,b=d.length,c=h.length,a,e=1,g=0,j,l,m,n;g-1?n:Math.max(j,l);if(l===-1)if(f){e+=1-f;continue}else return 0;else j=0.1;h[l]===m&&(j+=0.1);l===0?(j+=0.6,g===0&&(a=1)):h.charAt(l-1)===" "&&(j+=0.8);h=h.substring(l+1,c);i+=j}h=i/b;b=(h*(b/c)+h)/2;b/=e;a&&b+0.15<1&&(b+=0.15);return b}}}); +emmet.define("utils",function(h,d){function f(b){this._data=[];this.length=0;b&&this.append(b)}var i="${0}";f.prototype={append:function(b){this._data.push(b);this.length+=b.length},toString:function(){return this._data.join("")},valueOf:function(){return this.toString()}};return{reTag:/<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/,endsWithTag:function(b){return this.reTag.test(b)},isNumeric:function(b){typeof b=="string"&&(b=b.charCodeAt(0));return b&&b>47&& +b<58},trim:function(b){return(b||"").replace(/^\s+|\s+$/g,"")},getNewline:function(){var b=h("resources");if(!b)return"\n";b=b.getVariable("newline");return d.isString(b)?b:"\n"},setNewline:function(b){var c=h("resources");c.setVariable("newline",b);c.setVariable("nl",b)},splitByLines:function(b,c){var a=this.getNewline(),a=(b||"").replace(/\r\n/g,"\n").replace(/\n\r/g,"\n").replace(/\r/g,"\n").replace(/\n/g,a).split(a);c&&(a=d.filter(a,function(a){return a.length&&!!this.trim(a)},this));return a}, +normalizeNewline:function(b){return this.splitByLines(b).join(this.getNewline())},repeatString:function(b,c){for(var a=[],e=0;ee++;)a+="0";return a+b},unindentString:function(b, +c){for(var a=this.splitByLines(b),e=0;eb.length?b:b.substring(0,a)+c+b.substring(e)},narrowToNonSpace:function(b,c,a){c=h("range").create(c,a);for(a=/[\s\n\r\u00a0]/;c.start +c.start;)if(c.end--,!a.test(b.charAt(c.end))){c.end++;break}return c},findNewlineBounds:function(b,c){for(var a=b.length,e=0,g=a-1,d=c-1;d>0;d--){var f=b.charAt(d);if(f=="\n"||f=="\r"){e=d+1;break}}for(d=c;d":return b>c;case "gte":case ">=":return b>=c}}function i(b,c){d.isObject(b)&&"start"in b?(this.start=Math.min(b.start,b.end),this.end=Math.max(b.start,b.end)):d.isArray(b)?(this.start=b[0],this.end=b[1]):(c=d.isString(c)?c.length:+c,this.start=b,this.end=b+c)}i.prototype={length:function(){return Math.abs(this.end-this.start)}, +equal:function(b){return this.cmp(b,"eq","eq")},shift:function(b){this.start+=b;this.end+=b;return this},overlap:function(b){return b.start<=this.end&&b.end>=this.start},intersection:function(b){if(this.overlap(b)){var c=Math.max(b.start,this.start);return new i(c,Math.min(b.end,this.end)-c)}return null},union:function(b){if(this.overlap(b)){var c=Math.min(b.start,this.start);return new i(c,Math.max(b.end,this.end)-c)}return null},inside:function(b){return this.cmp(b,"lte","gt")},contains:function(b){return this.cmp(b, +"lt","gt")},include:function(){return this.cmp(loc,"lte","gte")},cmp:function(b,c,a){var e;b instanceof i?(e=b.start,b=b.end):e=b;return f(this.start,e,c||"<=")&&f(this.end,b,a||">")},substring:function(b){return this.length()>0?b.substring(this.start,this.end):""},clone:function(){return new i(this.start,this.length())},toArray:function(){return[this.start,this.end]},toString:function(){return"{"+this.start+", "+this.length()+"}"}};return{create:function(b,c){if(d.isUndefined(b)||b===null)return null; +if(b instanceof i)return b;if(d.isObject(b)&&"start"in b&&"end"in b)c=b.end-b.start,b=b.start;return new i(b,c)},create2:function(b,c){d.isNumber(b)&&d.isNumber(c)&&(c-=b);return this.create(b,c)}}}); +emmet.define("handlerList",function(h,d){function f(){this._list=[]}f.prototype={add:function(f,b){this._list.push(d.extend({order:0},b||{},{fn:f}))},remove:function(f){this._list=d.without(this._list,d.find(this._list,function(b){return b.fn===f}))},list:function(){return d.sortBy(this._list,"order").reverse()},listFn:function(){return d.pluck(this.list(),"fn")},exec:function(f,b){var b=b||[],c=null;d.find(this.list(),function(a){c=a.fn.apply(a,b);if(c!==f)return!0});return c}};return{create:function(){return new f}}}); +emmet.define("tokenIterator",function(h,d){function f(d){this.tokens=d;this._position=0;this.reset()}f.prototype={next:function(){if(this.hasNext()){var d=this.tokens[++this._i];this._position=d.start;return d}return null},current:function(){return this.tokens[this._i]},position:function(){return this._position},hasNext:function(){return this._i=this.string.length},sol:function(){return this.pos==0},peek:function(){return this.string.charAt(this.pos)},next:function(){if(this.posf},eatSpace:function(){for(var d=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>d},skipToEnd:function(){this.pos=this.string.length},skipTo:function(d){d=this.string.indexOf(d,this.pos);if(d>-1)return this.pos=d,!0},skipToPair:function(d,f){for(var h=0,b,c=this.pos,a=this.string.length;c/,c={},a={},e=h("handlerList").create();return{setVocabulary:function(b,e){i={};e== +"system"?c=b:a=b},getVocabulary:function(b){return b=="system"?c:a},getMatchedResource:function(a,b){return e.exec(null,d.toArray(arguments))||this.findSnippet(b,a.name())},getVariable:function(a){return(this.getSection("variables")||{})[a]},setVariable:function(a,b){var c=this.getVocabulary("user")||{};if(!("variables"in c))c.variables={};c.variables[a]=b;this.setVocabulary(c,"user")},hasSyntax:function(a){return a in this.getVocabulary("user")||a in this.getVocabulary("system")},addResolver:function(a, +b){e.add(a,b)},removeResolver:function(a){e.remove(a)},getSection:function(b){if(!b)return null;b in i||(i[b]=h("utils").deepMerge({},c[b],a[b]));for(var e=i[b],f=d.rest(arguments),m;e&&(m=f.shift());)if(m in e)e=e[m];else return null;return e},findItem:function(a,b){for(var c=this.getSection(a);c;){if(b in c)return c[b];c=this.getSection(c["extends"])}},findSnippet:function(a,b,c){if(!a||!b)return null;var c=c||[],e=[b];~b.indexOf("-")&&e.push(b.replace(/\-/g,":"));var h=this.getSection(a),i=null; +d.find(["snippets","abbreviations"],function(b){var c=this.getSection(a,b);if(c)return d.find(e,function(a){if(c[a])return i=f(a,c[a],b)})},this);c.push(a);return!i&&h["extends"]&&!d.include(c,h["extends"])?this.findSnippet(h["extends"],b,c):i},fuzzyFindSnippet:function(a,b,c){var c=c||0.3,a=this.getAllSnippets(a),e=h("string-score"),b=b.replace(/:$/,"").replace(/:/g,"-"),f=d.map(a,function(a,c){return{key:c,score:e.score(a.nk,b,0.1)}});if((f=d.last(d.sortBy(f,"score")))&&f.score>=c)return a[f.key].parsedValue}, +getAllSnippets:function(a){var b="all-"+a;if(!i[b]){var c=[],e=[];do{var h=this.getSection(a);if(!h)break;d.each(["snippets","abbreviations"],function(a){var b={};d.each(h[a]||null,function(c,e){b[e]={nk:e.replace(/:$/,"").replace(/:/g,"-"),value:c,parsedValue:f(e,c,a),type:a}});c.push(b)});e.push(a);a=h["extends"]}while(a&&!d.include(e,a));i[b]=d.extend.apply(d,c.reverse())}return i[b]}}}); +emmet.define("actions",function(h,d){function f(b){return h("utils").trim(b.charAt(0).toUpperCase()+b.substring(1).replace(/_[a-z]/g,function(b){return" "+b.charAt(1).toUpperCase()}))}var i={};return{add:function(b,c,a){b=b.toLowerCase();a=a||{};if(!a.label)a.label=f(b);i[b]={name:b,fn:c,options:a}},get:function(b){return i[b.toLowerCase()]},run:function(b,c){d.isArray(c)||(c=d.rest(arguments));var a=this.get(b);return a?a.fn.apply(emmet,c):(emmet.log('Action "%s" is not defined',b),!1)},getAll:function(){return i}, +getList:function(){return d.values(this.getAll())},getMenu:function(b){var c=[],b=b||[];d.each(this.getList(),function(a){if(!a.options.hidden&&!d.include(b,a.name)){var e=f(a.name),g=c;if(a.options.label)for(var j=a.options.label.split("/"),e=j.pop(),h,i;h=j.shift();)i=d.find(g,function(a){return a.type=="submenu"&&a.name==h}),i||(i={name:h,type:"submenu",items:[]},g.push(i)),g=i.items;g.push({type:"action",name:a.name,label:e})}});return c},getActionNameForMenuTitle:function(b,c){var a=null;d.find(c|| +this.getMenu(),function(c){if(c.type=="action"){if(c.label==b||c.name==b)return a=c.name}else return a=this.getActionNameForMenuTitle(b,c.items)},this);return a||null}}}); +emmet.define("profile",function(h,d){function f(a){d.extend(this,e,a)}function i(a,b){switch(String(b||"").toLowerCase()){case "lower":return a.toLowerCase();case "upper":return a.toUpperCase()}return a}function b(b,c){return a[b.toLowerCase()]=new f(c)}function c(){b("xhtml");b("html",{self_closing_tag:!1});b("xml",{self_closing_tag:!0,tag_nl:!0});b("plain",{tag_nl:!1,indent:!1,place_cursor:!1});b("line",{tag_nl:!1,indent:!1,extraFilters:"s"})}var a={},e={tag_case:"asis",attr_case:"asis",attr_quotes:"double", +tag_nl:"decide",tag_nl_leaf:!1,place_cursor:!0,indent:!0,inline_break:3,self_closing_tag:"xhtml",filters:"",extraFilters:""};f.prototype={tagName:function(a){return i(a,this.tag_case)},attributeName:function(a){return i(a,this.attr_case)},attributeQuote:function(){return this.attr_quotes=="single"?"'":'"'},selfClosing:function(){return this.self_closing_tag=="xhtml"?" /":this.self_closing_tag===!0?"/":""},cursor:function(){return this.place_cursor?h("utils").getCaretPlaceholder():""}};c();return{create:function(a, +c){return arguments.length==2?b(a,c):new f(d.defaults(a||{},e))},get:function(b,c){if(!b&&c){var e=h("resources").findItem(c,"profile");e&&(b=e)}return!b?a.plain:b instanceof f?b:d.isString(b)&&b.toLowerCase()in a?a[b.toLowerCase()]:this.create(b)},remove:function(b){b=(b||"").toLowerCase();b in a&&delete a[b]},reset:function(){a={};c()},stringCase:i}}); +emmet.define("editorUtils",function(h){return{isInsideTag:function(d,f){for(var h=/^<\/?\w[\w\:\-]*.*?>/,b=f;b>-1;){if(d.charAt(b)=="<")break;b--}return b!=-1&&(h=h.exec(d.substring(b)))&&f>b&&f"&&e.endsWithTag(d.substring(0,f+1)))){i=f+1;break}}return i!=-1&&!a&&!c&&!b?d.substring(i).replace(/^[\*\+\>\^]+/,""):""},getImageSize:function(d){var f=function(){return d.charCodeAt(h++)};if(d.substr(0,8)==="\u0089PNG\r\n\u001a\n"){var h=d.indexOf("IHDR")+4;return{width:f()<<24|f()<<16|f()<<8|f(),height:f()<<24|f()<<16|f()<<8|f()}}else if(d.substr(0,4)==="GIF8")return h=6,{width:f()|f()<<8,height:f()|f()<<8};else if(d.substr(0,2)==="\u00ff\u00d8")for(var h=2,b=d.length;h< +b;){if(f()!=255)break;var c=f();if(c==218)break;var a=f()<<8|f();if(c>=192&&c<=207&&!(c&4)&&!(c&8))return h+=1,{height:f()<<8|f(),width:f()<<8|f()};else h+=a-2}},captureContext:function(d){if(String(d.getSyntax())in{html:1,xml:1,xsl:1}){var f=String(d.getContent()),i=h("htmlMatcher").find(f,d.getCaretPos());if(i&&i.type=="tag"){for(var d=/([\w\-:]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g,i=i.open,f=i.range.substring(f).replace(/^<[\w\-\:]+/,""),i={name:i.name,attributes:[]}, +b;b=d.exec(f);)i.attributes.push({name:b[1],value:b[2]});return i}}return null},findExpressionBounds:function(d,f){for(var i=String(d.getContent()),b=i.length,c=d.getCaretPos()-1,a=c+1;c>=0&&f(i.charAt(c),c,i);)c--;for(;ac)return h("range").create([++c,a])},compoundUpdate:function(d,f){if(f){var h=d.getSelectionRange();d.replaceContent(f.data,f.start,f.end,!0);d.createSelection(f.caret,f.caret+h.end-h.start);return!0}return!1},detectSyntax:function(d,f){var i=f||"html"; +h("resources").hasSyntax(i)||(i="html");if(i=="html"&&(this.isStyle(d)||this.isInlineCSS(d)))i="css";return i},detectProfile:function(d){var f=d.getSyntax(),i=h("resources").findItem(f,"profile");if(i)return i;switch(f){case "xml":case "xsl":return"xml";case "css":if(this.isInlineCSS(d))return"line";break;case "html":return(i=h("resources").getVariable("profile"))||(i=this.isXHTML(d)?"xhtml":"html"),i}return"xhtml"},isXHTML:function(d){return d.getContent().search(/]+XHTML/i)!=-1},isStyle:function(d){var f= +String(d.getContent()),d=d.getCaretPos();return(f=h("htmlMatcher").tag(f,d))&&f.open.name.toLowerCase()=="style"&&f.innerRange.cmp(d,"lte","gte")},isInlineCSS:function(d){var f=String(d.getContent()),d=d.getCaretPos();return(f=h("xmlEditTree").parseFromPosition(f,d,!0))?(f=f.itemFromPosition(d,!0))&&f.name().toLowerCase()=="style"&&f.valueRange(!0).cmp(d,"lte","gte"):!1}}}); +emmet.define("abbreviationUtils",function(h,d){return{isSnippet:function(d){return h("elements").is(d.matchedResource(),"snippet")},isUnary:function(d){var i=d.matchedResource();return d.children.length||this.isSnippet(d)?!1:i&&i.is_empty||h("tagName").isEmptyElement(d.name())},isInline:function(d){return d.isTextNode()||!d.name()||h("tagName").isInlineLevel(d.name())},isBlock:function(d){return this.isSnippet(d)||!this.isInline(d)},isSnippet:function(d){return h("elements").is(d.matchedResource(), +"snippet")},hasTagsInContent:function(d){return h("utils").matchesTag(d.content)},hasBlockChildren:function(h){return this.hasTagsInContent(h)&&this.isBlock(h)||d.any(h.children,function(d){return this.isBlock(d)},this)},insertChildContent:function(f,i,b){var b=d.extend({keepVariable:!0,appendIfNoChild:!0},b||{}),c=!1,a=h("utils"),f=a.replaceVariables(f,function(e,d,h){var l=e;d=="child"&&(l=a.padString(i,a.getLinePaddingFromPosition(f,h.start)),c=!0,b.keepVariable&&(l+=e));return l});!c&&b.appendIfNoChild&& +(f+=i);return f}}}); +emmet.define("base64",function(){return{encode:function(h){for(var d=[],f,i,b,c,a,e,g=0,j=h.length;g>2,f=(f&3)<<4|i>>4,i=(i&15)<<2|b>>6,b&=63,isNaN(a)?i=b=64:isNaN(e)&&(b=64),d.push("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(c)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(i)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(b)); +return d.join("")},decode:function(h){var d,f,i,b,c,a=0,e=0,g=[],j=h.length;if(!h)return h;h+="";do d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(h.charAt(a++)),i=d<<18|f<<12|b<<6|c,d=i>>16& +255,f=i>>8&255,i&=255,b==64?g[e++]=String.fromCharCode(d):c==64?g[e++]=String.fromCharCode(d,f):g[e++]=String.fromCharCode(d,f,i);while(a")){n=h+3;break}if(h=b.matches(n))if(h.type=="open"&&!h.selfClose)c.push(h.name);else if(h.type=="close"){if(!c.length)return h.name==a.name?h:null;if(d.last(c)==h.name)c.pop();else{for(var q=!1;c.length&&!q;)c.pop()==h.name&&(q=!0);if(!c.length&&!q)return h.name==a.name?h:null}}}}var c=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/, +a=/^<\/([\w\:\-]+)[^>]*>/;return{find:function(a,c){for(var j=h("range"),l=f(a),m=null,n=null,o=c;o>=0;o--)if(m=l.open(o)){if(m.selfClose&&m.range.cmp(c,"lt","gt"))break;if(n=b(m,l)){if(j.create2(m.range.start,n.range.end).contains(c))break}else if(m.range.contains(c))break;m=null}else if(i(a,o,"--\>"))for(var q=o-1;q>=0;q--)if(i(a,q,"--\>"))break;else{if(i(a,q,"<\!--")){o=q;break}}else if(i(a,o,"<\!--")){q=o+4;for(m=a.length;q")){q+=3;break}m={range:h("range").create(o,d.isNumber(q)? +q-o:q[0]),type:"comment"};break}if(m)return q=o=null,n?(o=j.create2(m.range.start,n.range.end),q=j.create2(m.range.end,n.range.start)):o=q=j.create2(m.range.start,m.range.end),m.type=="comment"&&(j=o.substring(a),q.start+=j.length-j.replace(/^<\!--\s*/,"").length,q.end-=j.length-j.replace(/\s*--\>$/,"").length),{open:m,close:n,type:m.type=="comment"?"comment":"tag",innerRange:q,innerContent:function(){return this.innerRange.substring(a)},outerRange:o,outerContent:function(){return this.outerRange.substring(a)}, +range:!q.length()||!q.cmp(c,"lte","gte")?o:q,content:function(){return this.range.substring(a)},source:a}},tag:function(a,b){var c=this.find(a,b);if(c&&c.type=="tag")return c}}}); +emmet.define("tabStops",function(h,d){var f=100,i=0,b={replaceCarets:!1,escape:function(b){return"\\"+b},tabstop:function(b){return b.token},variable:function(b){return b.token}};h("abbreviationParser").addOutputProcessor(function(b,a){var e=0,d=h("tabStops"),f=h("utils"),l={tabstop:function(a){var b=parseInt(a.group);if(b==0)return"${0}";b>e&&(e=b);return a.placeholder?(b+=i,a=d.processText(a.placeholder,l),"${"+b+":"+a+"}"):"${"+(b+i)+"}"}},b=d.processText(b,l),b=f.replaceVariables(b,d.variablesResolver(a)); +i+=e+1;return b});return{extract:function(c,a){var e=h("utils"),g={carets:""},f=[],a=d.extend({},b,a,{tabstop:function(a){var b=a.token,c="";if(a.placeholder=="cursor")f.push({start:a.start,end:a.start+b.length,group:"carets",value:""});else{if("placeholder"in a)g[a.group]=a.placeholder;a.group in g&&(c=g[a.group]);f.push({start:a.start,end:a.start+b.length,group:a.group,value:c})}return b}});a.replaceCarets&&(c=c.replace(RegExp(e.escapeForRegexp(e.getCaretPlaceholder()),"g"),"${0:cursor}"));var c= +this.processText(c,a),i=e.stringBuilder(),m=0,e=d.map(f,function(a){i.append(c.substring(m,a.start));var b=i.length,e=g[a.group]||"";i.append(e);m=a.end;return{group:a.group,start:b,end:b+e.length}});i.append(c.substring(m));return{text:i.toString(),tabstops:d.sortBy(e,"start")}},processText:function(c,a){for(var a=d.extend({},b,a),e=h("utils").stringBuilder(),g=h("stringStream").create(c),f,i;f=g.next();)if(f=="\\"&&!g.eol())e.append(a.escape(g.next()));else{i=f;if(f=="$")if(g.start=g.pos-1,g.match(/^[0-9]+/))i= +a.tabstop({start:e.length,group:g.current().substr(1),token:g.current()});else if(f=g.match(/^\{([a-z_\-][\w\-]*)\}/))i=a.variable({start:e.length,name:f[1],token:g.current()});else if(f=g.match(/^\{([0-9]+)(:.+?)?\}/,!1)){g.skipToPair("{","}");i={start:e.length,group:f[1],token:g.current()};if(f=i.token.substring(i.group.length+2,i.token.length-1))i.placeholder=f.substr(1);i=a.tabstop(i)}e.append(i)}return e.toString()},upgrade:function(b,a){var e=0,g={tabstop:function(b){var c=parseInt(b.group); +c>e&&(e=c);return b.placeholder?"${"+(c+a)+":"+b.placeholder+"}":"${"+(c+a)+"}"}};d.each(["start","end","content"],function(a){b[a]=this.processText(b[a],g)},this);return e},variablesResolver:function(b){var a={},e=h("resources");return function(g,j){if(j=="child")return g;if(j=="cursor")return h("utils").getCaretPlaceholder();var i=b.attribute(j);if(!d.isUndefined(i))return i;if(i=e.getVariable(j))return i;a[j]||(a[j]=f++);return"${"+a[j]+":"+j+"}"}},resetPlaceholderCounter:function(){console.log("deprecated"); +f=100},resetTabstopIndex:function(){i=0;f=100}}}); +emmet.define("preferences",function(h,d){var f={},i={},b=null,c=null;return{define:function(a,b,c){var h=a;d.isString(a)&&(h={},h[a]={value:b,description:c});d.each(h,function(a,b){i[b]=d.isObject(a)&&"value"in a&&d.keys(a).length<3?a:{value:a}})},set:function(a,b){var c=a;d.isString(a)&&(c={},c[a]=b);d.each(c,function(a,b){if(!(b in i))throw'Property "'+b+'" is not defined. You should define it first with `define` method of current module';if(a!==i[b].value){switch(typeof i[b].value){case "boolean":var c= +a;d.isString(c)?(c=c.toLowerCase(),a=c=="yes"||c=="true"||c=="1"):a=!!c;break;case "number":a=parseInt(a+"",10)||0;break;default:a+=""}f[b]=a}else b in f&&delete f[b]})},get:function(a){if(a in f)return f[a];if(a in i)return i[a].value},getArray:function(a){a=this.get(a);d.isUndefined(a)||(a=d.map(a.split(","),h("utils").trim),a.length||(a=null));return a},getDict:function(a){var b={};d.each(this.getArray(a),function(a){a=a.split(":");b[a[0]]=a[1]});return b},description:function(a){return a in i? +i[a].description:void 0},remove:function(a){d.isArray(a)||(a=[a]);d.each(a,function(a){a in f&&delete f[a];a in i&&delete i[a]})},list:function(){return d.map(d.keys(i).sort(),function(a){return{name:a,value:this.get(a),type:typeof i[a].value,description:i[a].description}},this)},load:function(a){d.each(a,function(a,b){this.set(b,a)},this)},exportModified:function(){return d.clone(f)},reset:function(){f={}},_startTest:function(){b=i;c=f;i={};f={}},_stopTest:function(){i=b;f=c}}}); +emmet.define("filters",function(h,d){function f(b){return!b?[]:d.isString(b)?b.split(/[\|,]/g):b}var i={};return{add:function(b,c){i[b]=c},apply:function(b,c,a){var e=h("utils"),a=h("profile").get(a);d.each(f(c),function(c){(c=e.trim(c.toLowerCase()))&&c in i&&(b=i[c](b,a))});return b},composeList:function(b,c,a){c=h("profile").get(c);b=f(c.filters||h("resources").findItem(b,"filters")||"html");c.extraFilters&&(b=b.concat(f(c.extraFilters)));a&&(b=b.concat(f(a)));if(!b||!b.length)b=f("html");return b}, +extractFromAbbreviation:function(b){var c="",b=b.replace(/\|([\w\|\-]+)$/,function(a,b){c=b;return""});return[b,f(c)]}}}); +emmet.define("elements",function(h,d){function f(a){return{data:a}}var i={},b=/([\w\-]+)\s*=\s*(['"])(.*?)\2/g,c={add:function(a,b){var c=this;i[a]=function(){var d=b.apply(c,arguments);if(d)d.type=a;return d}},get:function(a){return i[a]},create:function(a){var b=[].slice.call(arguments,1),c=this.get(a);return c?c.apply(this,b):null},is:function(a,b){return a&&a.type===b}};c.add("element",function(a,c,g){var h={name:a,is_empty:!!g};if(c)if(h.attributes=[],d.isArray(c))h.attributes=c;else if(d.isString(c))for(;a= +b.exec(c);)h.attributes.push({name:a[1],value:a[3]});else d.each(c,function(a,b){h.attributes.push({name:b,value:a})});return h});c.add("snippet",f);c.add("reference",f);c.add("empty",function(){return{}});return c}); +emmet.define("editTree",function(h,d,f){function i(a,b){this.options=d.extend({offset:0},b);this.source=a;this._children=[];this._positions={name:0};this.initialize.apply(this,arguments)}function b(a,b,c){this.parent=a;this._name=b.value;this._value=c?c.value:"";this._positions={name:b.start,value:c?c.start:-1};this.initialize.apply(this,arguments)}var c=h("range").create;i.extend=f.extend;i.prototype={initialize:function(){},_updateSource:function(a,b,g){var f=c(b,d.isUndefined(g)?0:g-b),i=a.length- +f.length(),m=function(a){d.each(a,function(b,c){b>=f.end&&(a[c]+=i)})};m(this._positions);d.each(this.list(),function(a){m(a._positions)});this.source=h("utils").replaceSubstring(this.source,a,f)},add:function(a,c){var d=new b(a,c);this._children.push(d);return d},get:function(a){return d.isNumber(a)?this.list()[a]:d.isString(a)?d.find(this.list(),function(b){return b.name()===a}):a},getAll:function(a){d.isArray(a)||(a=[a]);var b=[],c=[];d.each(a,function(a){d.isString(a)?b.push(a):d.isNumber(a)&& +c.push(a)});return d.filter(this.list(),function(a,h){return d.include(c,h)||d.include(b,a.name())})},value:function(a,b,c){var h=this.get(a);if(h)return h.value(b);if(!d.isUndefined(b))return this.add(a,b,c)},values:function(a){return d.map(this.getAll(a),function(a){return a.value()})},remove:function(a){if(a=this.get(a))this._updateSource("",a.fullRange()),this._children=d.without(this._children,a)},list:function(){return this._children},indexOf:function(a){return d.indexOf(this.list(),this.get(a))}, +name:function(a){if(!d.isUndefined(a)&&this._name!==(a=String(a)))this._updateSource(a,this._positions.name,this._positions.name+this._name.length),this._name=a;return this._name},nameRange:function(a){return c(this._positions.name+(a?this.options.offset:0),this.name())},range:function(a){return c(a?this.options.offset:0,this.toString())},itemFromPosition:function(a,b){return d.find(this.list(),function(c){return c.range(b).inside(a)})},toString:function(){return this.source}};b.extend=f.extend;b.prototype= +{initialize:function(){},_pos:function(a,b){return a+(b?this.parent.options.offset:0)},value:function(a){if(!d.isUndefined(a)&&this._value!==(a=String(a)))this.parent._updateSource(a,this.valueRange()),this._value=a;return this._value},name:function(a){if(!d.isUndefined(a)&&this._name!==(a=String(a)))this.parent._updateSource(a,this.nameRange()),this._name=a;return this._name},namePosition:function(a){return this._pos(this._positions.name,a)},valuePosition:function(a){return this._pos(this._positions.value, +a)},range:function(a){return c(this.namePosition(a),this.toString())},fullRange:function(a){return this.range(a)},nameRange:function(a){return c(this.namePosition(a),this.name())},valueRange:function(a){return c(this.valuePosition(a),this.value())},toString:function(){return this.name()+this.value()},valueOf:function(){return this.toString()}};return{EditContainer:i,EditElement:b,createToken:function(a,b,c){a={start:a||0,value:b||"",type:c};a.end=a.start+a.value.length;return a}}}); +emmet.define("cssEditTree",function(h,d){function f(a,b){return h("range").create(a,b)}function i(a,b){var b=b||e|g,c=["white","line"];if((b&g)==g)for(;a.length&&d.include(c,d.last(a).type);)a.pop();if((b&e)==e)for(;a.length&&d.include(c,a[0].type);)a.shift();return a}function b(a){var b=["white","line",":"],c=[],h,j;a.nextUntil(function(){return!d.include(b,this.itemNext().type)});for(j=a.current().end;h=a.next();){if(h.type=="}"||h.type==";")return i(c,e|(h.type=="}"?g:0)),c.length?(j=c[0].start, +a=d.last(c).end):a=j,f(j,a-j);c.push(h)}if(c.length)return f(c[0].start,d.last(c).end-c[0].start)}function c(a){var b=h("stringStream").create(a),c=[],e=/[\s\u00a0,]/,g=function(){b.next();c.push(f(b.start,b.current()));b.start=b.pos};b.eatSpace();for(b.start=b.pos;a=b.next();)if(a=='"'||a=="'"){b.next();if(!b.skipTo(a))break;g()}else if(a=="("){b.backUp(1);if(!b.skipToPair("(",")"))break;b.backUp(1);g()}else if(e.test(a))c.push(f(b.start,b.current().length-1)),b.eatWhile(e),b.start=b.pos;g();return d.chain(c).filter(function(a){return!!a.length()}).uniq(!1, +function(a){return a.toString()}).value()}var a={styleBefore:"\n\t",styleSeparator:": ",offset:0},e=1,g=2,j=h("editTree").EditContainer.extend({initialize:function(c){d.defaults(this.options,a);var e=h("editTree"),g=h("tokenIterator").create(h("cssParser").parse(c)),j,s=[],r;for(j=g.position();r=g.next();){if(r.type=="{")break;s.push(r)}i(s);s.length?(j=s[0].start,s=d.last(s).end):s=j;j=f(j,s-j);this._positions.name=j.start;this._name=j.substring(c);if(!g.current()||g.current().type!="{")throw"Invalid CSS rule"; +for(this._positions.contentStart=g.position()+1;j=g.next();){if(s=j.type=="identifier")a:{s=g.tokens;r=g._i+1;for(var u=s.length;r1)e.styleBefore="\n"+d.last(g);e.styleSeparator=b.substring(e.nameRange().end,e.valuePosition());e.styleBefore=d.last(e.styleBefore.split("*/"));e.styleSeparator=e.styleSeparator.replace(/\/\*.*?\*\//g,"");a=e.range().end})},add:function(a,b,c){var e=this.list(),g=this._positions.contentStart,f=d.pick(this.options,"styleBefore", +"styleSeparator"),j=h("editTree");if(d.isUndefined(c))c=e.length;var i=e[c];if(i)g=i.fullRange().start;else if(i=e[c-1])i.end(";"),g=i.range().end;i&&(f=d.pick(i,"styleBefore","styleSeparator"));a=j.createToken(g+f.styleBefore.length,a);b=j.createToken(a.end+f.styleSeparator.length,b);j=new l(this,a,b,j.createToken(b.end,";"));d.extend(j,f);this._updateSource(j.styleBefore+j.toString(),g);this._children.splice(c,0,j);return j}}),l=h("editTree").EditElement.extend({initialize:function(a,b,c,d){this.styleBefore= +a.options.styleBefore;this.styleSeparator=a.options.styleSeparator;this._end=d.value;this._positions.end=d.start},valueParts:function(a){var b=c(this.value());if(a){var e=this.valuePosition(!0);d.each(b,function(a){a.shift(e)})}return b},end:function(a){if(!d.isUndefined(a)&&this._end!==a)this.parent._updateSource(a,this._positions.end,this._positions.end+this._end.length),this._end=a;return this._end},fullRange:function(a){a=this.range(a);a.start-=this.styleBefore.length;return a},toString:function(){return this.name()+ +this.styleSeparator+this.value()+this.end()}});return{parse:function(a,b){return new j(a,b)},parseFromPosition:function(a,b,c){c=this.extractRule(a,b,c);return!c||!c.inside(b)?null:this.parse(c.substring(a),{offset:c.start})},extractRule:function(a,b,c){for(var d="",e=a.length,g=-1,f;b>=0;){f=a.charAt(b);if(f=="{"){g=b;break}else if(f=="}"&&!c){b++;break}b--}for(;b=0;){f=a.charAt(b);if("{}/\\<>\n\r".indexOf(f)!= +-1)break;b--}c=a.substring(b+1,g).replace(/^[\s\n\r]+/m,"");return h("range").create(g-c.length,d.length+c.length)}return null},baseName:function(a){return a.replace(/^\s*\-\w+\-/,"")},findParts:c}}); +emmet.define("xmlEditTree",function(h,d){var f={styleBefore:" ",styleSeparator:"=",styleQuote:'"',offset:0},i=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/m,b=h("editTree").EditContainer.extend({initialize:function(a){d.defaults(this.options,f);this._positions.name=1;var b=null,g=h("xmlParser").parse(a),j=h("range");d.each(g,function(d){d.value=j.create(d).substring(a);switch(d.type){case "tag":if(/^<[^\/]+/.test(d.value))this._name=d.value.substring(1); +break;case "attribute":b&&this._children.push(new c(this,b));b=d;break;case "string":this._children.push(new c(this,b,d)),b=null}},this);b&&this._children.push(new c(this,b));this._saveStyle()},_saveStyle:function(){var a=this.nameRange().end,b=this.source;d.each(this.list(),function(c){c.styleBefore=b.substring(a,c.namePosition());if(c.valuePosition()!==-1)c.styleSeparator=b.substring(c.namePosition()+c.name().length,c.valuePosition()-c.styleQuote.length);a=c.range().end})},add:function(a,b,g){var f= +this.list(),i=this.nameRange().end,m=h("editTree"),n=d.pick(this.options,"styleBefore","styleSeparator","styleQuote");if(d.isUndefined(g))g=f.length;var o=f[g];if(o)i=o.fullRange().start;else if(o=f[g-1])i=o.range().end;o&&(n=d.pick(o,"styleBefore","styleSeparator","styleQuote"));b=n.styleQuote+b+n.styleQuote;a=new c(this,m.createToken(i+n.styleBefore.length,a),m.createToken(i+n.styleBefore.length+a.length+n.styleSeparator.length,b));d.extend(a,n);this._updateSource(a.styleBefore+a.toString(),i); +this._children.splice(g,0,a);return a}}),c=h("editTree").EditElement.extend({initialize:function(a,b,c){this.styleBefore=a.options.styleBefore;this.styleSeparator=a.options.styleSeparator;b="";a=a.options.styleQuote;if(c)b=c.value,a=b.charAt(0),a=='"'||a=="'"?b=b.substring(1):a="",a&&b.charAt(b.length-1)==a&&(b=b.substring(0,b.length-1));this.styleQuote=a;this._value=b;this._positions.value=c?c.start+a.length:-1},fullRange:function(a){a=this.range(a);a.start-=this.styleBefore.length;return a},toString:function(){return this.name()+ +this.styleSeparator+this.styleQuote+this.value()+this.styleQuote}});return{parse:function(a,c){return new b(a,c)},parseFromPosition:function(a,b,c){c=this.extractTag(a,b,c);return!c||!c.inside(b)?null:this.parse(c.substring(a),{offset:c.start})},extractTag:function(a,b,c){var d=a.length,f,m=h("range"),n=Math.min(2E3,d),o=null,q=function(b){var c;if(a.charAt(b)=="<"&&(c=a.substr(b,n).match(i)))return m.create(b,c[0])};for(f=b;f>=0;f--)if(o=q(f))break;if(o&&(o.inside(b)||c))return o;if(!o&&c)return null; +for(f=b;f=a++;)if(b.substr(a,g.length)==g){i=a+g.length;break}}return f!=-1&&i!=-1?h("range").create(f,i-f):null}function b(b,a,d,g){function f(b){return b.replace(RegExp("^"+q.escapeForRegexp(a)+"\\s*"), +function(a){n-=a.length;return""}).replace(RegExp("\\s*"+q.escapeForRegexp(d)+"$"),"")}var l=h("editorUtils"),m=l.outputInfo(b).content,n=b.getCaretPos(),o=null,q=h("utils");(o=i(m,n,a,d))&&o.overlap(g)?(g=o,o=f(g.substring(m))):(o=a+" "+g.substring(m).replace(RegExp(q.escapeForRegexp(a)+"\\s*|\\s*"+q.escapeForRegexp(d),"g"),"")+" "+d,n+=a.length+1);return o!==null?(o=q.escapeText(o),b.setCaretPos(g.start),b.replaceContent(l.unindent(b,o),g.start,g.end),b.setCaretPos(n),!0):!1}h("actions").add("toggle_comment", +function(c){var a=h("editorUtils").outputInfo(c);if(a.syntax=="css"){var d=c.getCaretPos(),g=h("htmlMatcher").tag(a.content,d);if(g&&g.open.range.inside(d))a.syntax="html"}if(a.syntax=="css"){g=h("range").create(c.getSelectionRange());a=h("editorUtils").outputInfo(c);if(!g.length()&&(d=h("cssEditTree").parseFromPosition(a.content,c.getCaretPos())))g=(g=f(d,c.getCaretPos()))?g.range(!0):h("range").create(d.nameRange(!0).start,d.source);g.length()||(g=h("range").create(c.getCurrentLineRange()),h("utils").narrowToNonSpace(a.content, +g));c=b(c,"/*","*/",g)}else{a=h("range").create(c.getSelectionRange());d=h("editorUtils").outputInfo(c);if(!a.length()&&(d=h("htmlMatcher").tag(d.content,c.getCaretPos())))a=d.outerRange;c=b(c,"<\!--","--\>",a)}return c})}); +emmet.exec(function(h){function d(d,h,b){function c(b){for(var c=b;c>=0;){var d=a.charAt(c);if(d=="\n"||d=="\r")break;c--}return a.substring(c,b)}for(var h=h||1,b=d.getCaretPos()+(b||0),a=String(d.getContent()),d=a.length,e=-1,g=/^\s+$/;b<=d&&b>=0;){b+=h;var j=a.charAt(b),l=a.charAt(b+1),m=a.charAt(b-1);switch(j){case '"':case "'":l==j&&m=="="&&(e=b+1);break;case ">":l=="<"&&(e=b+1);break;case "\n":case "\r":g.test(c(b-1))&&(e=b)}if(e!=-1)break}return e}h=h("actions");h.add("prev_edit_point",function(f){var h= +f.getCaretPos(),b=d(f,-1);b==h&&(b=d(f,-1,-2));return b!=-1?(f.setCaretPos(b),!0):!1},{label:"Previous Edit Point"});h.add("next_edit_point",function(f){var h=d(f,1);return h!=-1?(f.setCaretPos(h),!0):!1})}); +emmet.exec(function(h,d){function f(a,b,c,d){var e=h("range"),g=h("editorUtils").outputInfo(a).content,f=g.length,j,i=e.create(-1,0),l=e.create(a.getSelectionRange());j=l.start;for(var o=1E5;j>=0&&j0;){if(e=c(g,j,b)){if(i.equal(e))break;i=e.clone();if(j=d(e.substring(g),e.start,l.clone()))return a.createSelection(j.start,j.end),!0;else j=b?e.start:e.end-1}j+=b?-1:1}return!1}function i(a){var b=!0;return f(a,!1,function(a,c){if(b){b=!1;var d;a:{d=c;for(var e;d>=0;){if(e=g(a,d)){d=e;break a}d--}d= +null}return d}else return g(a,c)},function(a,b,c){return e(a,b,c,!1)})}function b(a){return f(a,!0,g,function(a,b,c){return e(a,b,c,!0)})}function c(b,c,e){var e=e||0,g=h("range"),f=[],i=-1,l="",z="",x,o;d.each(c,function(c){switch(c.type){case "tag":o=b.substring(c.start,c.end);/^<[\w\:\-]/.test(o)&&f.push(g.create({start:c.start+1,end:c.end}));break;case "attribute":i=c.start;l=b.substring(c.start,c.end);break;case "string":f.push(g.create(i,c.end-i)),x=g.create(c),z=x.substring(b),j(z.charAt(0))&& +x.start++,j(z.charAt(z.length-1))&&x.end--,f.push(x),l=="class"&&(f=f.concat(a(x.substring(b),x.start)))}});d.each(f,function(a){a.shift(e)});return d.chain(f).filter(function(a){return!!a.length()}).uniq(!1,function(a){return a.toString()}).value()}function a(a,b){var b=b||0,c=[],d=h("stringStream").create(a),e=h("range");d.eatSpace();d.start=d.pos;for(var g;g=d.next();)if(/[\s\u00a0]/.test(g))c.push(e.create(d.start+b,d.pos-d.start-1)),d.eatSpace(),d.start=d.pos;c.push(e.create(d.start+b,d.pos- +d.start));return c}function e(a,b,e,g){a=c(a,h("xmlParser").parse(a),b);g&&a.reverse();return(b=d.find(a,function(a){return a.equal(e)}))?(g=d.indexOf(a,b),g1)?g[1]:d.find(a,function(a){return a.end>e.end})}function g(a,b){var c;if(a.charAt(b)=="<"&&(c=a.substring(b,a.length).match(q)))return h("range").create(b,c[0])}function j(a){return a=='"'||a=="'"}function l(a){var b= +a.valueRange(!0),c=[a.range(!0),b],e=h("stringStream"),g=h("cssEditTree"),f=h("range"),j=a.value();d.each(a.valueParts(),function(a){var h=a.clone();c.push(h.shift(b.start));var i=e.create(a.substring(j));if(i.match(/^[\w\-]+\(/,!0)){i.start=i.pos;i.skipToPair("(",")");var l=i.current();c.push(f.create(h.start+i.start,l));d.each(g.findParts(l),function(a){c.push(f.create(h.start+i.start+a.start,a.substring(l)))})}});return d.chain(c).filter(function(a){return!!a.length()}).uniq(!1,function(a){return a.toString()}).value()} +function m(a,b,c){var e=null,g=null,f=a.list(),h,j;c?(f.reverse(),h=function(a){return a.range(!0).start<=b.start},j=function(a){return a.start=b.end},j=function(a){return a.end>b.start});for(;e=d.find(f,h);){a=l(e);c&&a.reverse();if(g=d.find(a,function(a){return a.equal(b)})){if(g=d.indexOf(a,g),g!=a.length-1){g=a[g+1];break}}else{g=d.filter(a,function(a){return a.inside(b.end)});if(g.length>1){g=g[1];break}if(g=d.find(a,j))break}g=null;b.start=b.end= +c?e.range(!0).start-1:e.range(!0).end+1}return g}function n(a,b,c){a=h("cssEditTree").parse(a,{offset:b});b=a.nameRange(!0);return c.endb.start)?b:a}var q=/^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,s=h("actions");s.add("select_next_item",function(a){return a.getSyntax()=="css"?f(a,!1,h("cssEditTree").extractRule,n):i(a)});s.add("select_previous_item", +function(a){return a.getSyntax()=="css"?f(a,!0,h("cssEditTree").extractRule,o):b(a)})}); +emmet.exec(function(h){function d(c,a){var a=String((a||"out").toLowerCase()),d=h("editorUtils").outputInfo(c),g=h("range").create(c.getSelectionRange()),d=d.content;b&&!b.range.equal(g)&&(b=null);if(b&&g.length())if(a=="in")if(b.type=="tag"&&!b.close)return!1;else if(b.range.equal(b.outerRange))b.range=b.innerRange;else{var f=h("utils").narrowToNonSpace(d,b.innerRange);if((b=i.find(d,f.start+1))&&b.range.equal(g)&&b.outerRange.equal(g))b.range=b.innerRange}else if(!b.innerRange.equal(b.outerRange)&& +b.range.equal(b.innerRange)&&g.equal(b.range))b.range=b.outerRange;else{if((b=i.find(d,g.start))&&b.range.equal(g)&&b.innerRange.equal(g))b.range=b.outerRange}else b=i.find(d,g.start);if(b&&!b.range.equal(g))return c.createSelection(b.range.start,b.range.end),!0;b=null;return!1}var f=h("actions"),i=h("htmlMatcher"),b=null;f.add("match_pair",d,{hidden:!0});f.add("match_pair_inward",function(b){return d(b,"in")},{label:"HTML/Match Pair Tag (inward)"});f.add("match_pair_outward",function(b){return d(b, +"out")},{label:"HTML/Match Pair Tag (outward)"});f.add("matching_pair",function(b){var a=String(b.getContent()),d=b.getCaretPos();a.charAt(d)=="<"&&d++;return(a=i.tag(a,d))&&a.close?(a.open.range.inside(d)?b.setCaretPos(a.close.range.start):b.setCaretPos(a.open.range.start),!0):!1},{label:"HTML/Go To Matching Tag Pair"})}); +emmet.exec(function(h){h("actions").add("remove_tag",function(d){var f=h("utils"),i=h("editorUtils").outputInfo(d),b=h("htmlMatcher").tag(i.content,d.getCaretPos());if(b){if(b.close){var c=f.narrowToNonSpace(i.content,b.innerRange),a=f.findNewlineBounds(i.content,c.start),a=f.getLinePadding(a.substring(i.content)),i=c.substring(i.content),i=f.unindentString(i,a);d.replaceContent(f.getCaretPlaceholder()+f.escapeText(i),b.outerRange.start,b.outerRange.end)}else d.replaceContent(f.getCaretPlaceholder(), +b.range.start,b.range.end);return!0}return!1},{label:"HTML/Remove Tag"})}); +emmet.exec(function(h){h("actions").add("split_join_tag",function(d,f){var i=h("htmlMatcher"),b=h("editorUtils").outputInfo(d,null,f),c=h("profile").get(b.profile);if(i=i.tag(b.content,d.getCaretPos())){if(i.close){var b=h("utils"),c=c.selfClosing()||" /",c=i.open.range.substring(i.source).replace(/\s*>$/,c+">"),a=d.getCaretPos();c.length+i.outerRange.start$/,">"),a=i.outerRange.start+e.length;e+=c+"";e=b.escapeText(e);d.replaceContent(e,i.outerRange.start,i.outerRange.end)}d.setCaretPos(a);i=!0}else i=!1;return i},{label:"HTML/Split\\Join Tag Declaration"})}); +emmet.define("reflectCSSValue",function(h,d){function f(c){var a=h("cssEditTree"),d=h("editorUtils").outputInfo(c),c=c.getCaretPos();if(a=a.parseFromPosition(d.content,c))if(d=a.itemFromPosition(c,!0)){var g=a.source,f=a.options.offset,c=c-f-d.range().start;b.exec(!1,[d]);if(g!==a.source)return{data:a.source,start:f,end:f+g.length,caret:f+d.range().start+c}}}function i(b){var b=h("cssEditTree").baseName(b),a;if(b=="opacity"||b=="filter")return/^(?:\-\w+\-)?(?:opacity|filter)$/;else if(a=b.match(/^border-radius-(top|bottom)(left|right)/))return RegExp("^(?:\\-\\w+\\-)?(?:"+ +b+"|border-"+a[1]+"-"+a[2]+"-radius)$");else if(a=b.match(/^border-(top|bottom)-(left|right)-radius/))return RegExp("^(?:\\-\\w+\\-)?(?:"+b+"|border-radius-"+a[1]+a[2]+")$");return RegExp("^(?:\\-\\w+\\-)?"+b+"$")}var b=h("handlerList").create();h("actions").add("reflect_css_value",function(b){return b.getSyntax()!="css"?!1:h("actionUtils").compoundUpdate(b,f(b))},{label:"CSS/Reflect Value"});b.add(function(b){var a=i(b.name());d.each(b.parent.list(),function(d){if(a.test(d.name())){var g;var f=b.name(), +i=b.value(),m=d.name();g=d.value();var n=h("cssEditTree"),o=h("utils"),f=n.baseName(f),m=n.baseName(m);g=f=="opacity"&&m=="filter"?g.replace(/opacity=[^)]*/i,"opacity="+Math.floor(parseFloat(i)*100)):f=="filter"&&m=="opacity"?(f=i.match(/opacity=([^)]*)/i))?o.prettifyNumber(parseInt(f[1])/100):g:i;d.value(g)}})},{order:-1});return{addHandler:function(c,a){b.add(c,a)},removeHandler:function(c){b.remove(c,options)}}}); +emmet.exec(function(h){h("actions").add("evaluate_math_expression",function(d){var f=h("actionUtils"),i=h("utils"),b=String(d.getContent()),c=h("range").create(d.getSelectionRange());c.length()||(c=f.findExpressionBounds(d,function(a){return i.isNumeric(a)||".+-*/\\".indexOf(a)!=-1}));if(c&&c.length()){f=c.substring(b);f=f.replace(/([\d\.\-]+)\\([\d\.\-]+)/g,"Math.round($1/$2)");try{var a=i.prettifyNumber((new Function("return "+f))());d.replaceContent(a,c.start,c.end);d.setCaretPos(c.start+a.length); +return!0}catch(e){}}return!1},{label:"Numbers/Evaluate Math Expression"})}); +emmet.exec(function(h,d){function f(b,a){var e=h("utils"),g=!1,f=!1,l=h("actionUtils").findExpressionBounds(b,function(a,b,c){return e.isNumeric(a)?!0:a=="."?!e.isNumeric(c.charAt(b+1))?!1:f?!1:f=!0:a=="-"?g?!1:g=!0:!1});if(l&&l.length()){var m=l.substring(String(b.getContent())),n=parseFloat(m);if(!d.isNaN(n)){n=e.prettifyNumber(n+a);if(/^(\-?)0+[1-9]/.test(m)){var o="";RegExp.$1&&(o="-",n=n.substring(1));n=n.split(".");n[0]=e.zeroPadString(n[0],i(m));n=o+n.join(".")}b.replaceContent(n,l.start,l.end); +b.createSelection(l.start,l.start+n.length);return!0}}return!1}function i(b){b=b.replace(/^\-/,"");return~b.indexOf(".")?b.split(".")[0].length:b.length}var b=h("actions");d.each([1,-1,10,-10,0.1,-0.1],function(c){var a=c>0?"increment":"decrement";b.add(a+"_number_by_"+String(Math.abs(c)).replace(".","").substring(0,2),function(a){return f(a,c)},{label:"Numbers/"+a.charAt(0).toUpperCase()+a.substring(1)+" number by "+Math.abs(c)})})}); +emmet.exec(function(h,d){var f=h("actions"),i=h("preferences");i.define("css.closeBraceIndentation","\n","Indentation before closing brace of CSS rule. Some users prefere indented closing brace of CSS rule for better readability. This preference\u2019s value will be automatically inserted before closing brace when user adds newline in newly created CSS rule (e.g. when \u201cInsert formatted linebreak\u201d action will be performed in CSS file). If you\u2019re such user, you may want to write put a value like \\n\\t in this preference."); +f.add("insert_formatted_line_break_only",function(b){var c=h("utils"),a=h("resources"),e=h("editorUtils").outputInfo(b),g=b.getCaretPos(),f=c.getNewline();if(d.include(["html","xml","xsl"],e.syntax)){if(a=a.getVariable("indentation"),(e=h("htmlMatcher").tag(e.content,g))&&!e.innerRange.length())return b.replaceContent(f+a+c.getCaretPlaceholder()+f,g),!0}else if(e.syntax=="css"&&(e=e.content,g&&e.charAt(g-1)=="{")){var l=i.get("css.closeBraceIndentation"),a=a.getVariable("indentation"),m=e.charAt(g)== +"}";if(!m)for(var n=g,o=e.length,q;na.length?b.replaceContent(c+l,g,g,!0):b.replaceContent(c,g)}return!0},{hidden:!0})}); +emmet.exec(function(h){h("actions").add("merge_lines",function(d){var f=h("htmlMatcher"),i=h("utils"),b=h("editorUtils").outputInfo(d),c=h("range").create(d.getSelectionRange());if(!c.length()&&(f=f.find(b.content,d.getCaretPos())))c=f.outerRange;if(c.length()){b=c.substring(b.content);b=i.splitByLines(b);for(f=1;f=0;)if(d("src=",c,b)){if(c=c.substr(b).match(/^(src=(["'])?)([^'"<>\s]+)\1?/))i=c[3],b+=c[1].length;break}else if(d("url(",c,b)){if(c=c.substr(b).match(/^(url\((['"])?)([^'"\)\s]+)\1?/))i=c[3],b+=c[1].length;break}if(i)if(d("data:",i))if(c=String(f.prompt("Enter path to file (absolute or relative)"))){var a= +h("file"),e=a.createPath(f.getFilePath(),c);if(!e)throw"Can't save file";a.save(e,h("base64").decode(i.replace(/^data\:.+?;.+?,/,"")));f.replaceContent("$0"+c,b,b+i.length);f=!0}else f=!1;else{c=h("file");a=h("actionUtils");e=f.getFilePath();if(e===null)throw"You should save your file before using this action";e=c.locateFile(e,i);if(e===null)throw"Can't find "+i+" file";var g=h("base64").encode(String(c.read(e)));if(!g)throw"Can't encode file content to base64";g="data:"+(a.mimeTypes[String(c.getExt(e))]|| +"application/octet-stream")+";base64,"+g;f.replaceContent("$0"+g,b,b+i.length);f=!0}else f=!1;return f},{label:"Encode\\Decode data:URL image"})}); +emmet.exec(function(h,d){function f(d,b){var c;if(b){if(/^data:/.test(b))c=h("base64").decode(b.replace(/^data\:.+?;.+?,/,""));else{c=h("file");var a=c.locateFile(d.getFilePath(),b);if(a===null)throw"Can't find "+b+" file";c=String(c.read(a))}return h("actionUtils").getImageSize(c)}}h("actions").add("update_image_size",function(i){var b;if(String(i.getSyntax())=="css")a:{b=i.getCaretPos();var c=h("editorUtils").outputInfo(i);if(c=h("cssEditTree").parseFromPosition(c.content,b,!0)){var a=c.itemFromPosition(b, +!0),e;if(a&&(e=/url\((["']?)(.+?)\1\)/i.exec(a.value()||"")))if(e=f(i,e[2])){a=c.range(!0);c.value("width",e.width+"px");c.value("height",e.height+"px",c.indexOf("width")+1);b=d.extend(a,{data:c.toString(),caret:b});break a}}b=null}else a:{b=i.getCaretPos();c=h("editorUtils").outputInfo(i);if((c=h("xmlEditTree").parseFromPosition(c.content,b,!0))&&(c.name()||"").toLowerCase()=="img")if(e=f(i,c.value("src"))){a=c.range(!0);c.value("width",e.width);c.value("height",e.height,c.indexOf("width")+1);b= +d.extend(a,{data:c.toString(),caret:b});break a}b=null}return h("actionUtils").compoundUpdate(i,b)})}); +emmet.define("cssResolver",function(h,d){function f(a){var b=a&&a.charCodeAt(0);return a&&a=="."||b>47&&b<58}function i(a){a=h("utils").trim(a);if(~a.indexOf("/*")||/[\n\r]/.test(a))return!1;if(!/^[a-z0-9\-]+\s*\:/i.test(a))return!1;a=h("tabStops").processText(a,{replaceCarets:!0,tabstop:function(){return"value"}});return a.split(":").length==2}function b(a){a.charAt(0);if(a.charAt(0)=="#"){var b=a.replace(/^#+/,"")||"0",d=h("utils").repeatString,a=null;switch(b.length){case 1:a=d(b,6);break;case 2:a= +d(b,3);break;case 3:a=b.charAt(0)+b.charAt(0)+b.charAt(1)+b.charAt(1)+b.charAt(2)+b.charAt(2);break;case 4:a=b+b.substr(0,2);break;case 5:a=b+b.charAt(0);break;default:a=b.substr(0,6)}p.get("css.color.short")&&(b=a.split(""),b[0]==b[1]&&b[2]==b[3]&&b[4]==b[5]&&(a=b[0]+b[2]+b[4]));switch(p.get("css.color.case")){case "upper":a=a.toUpperCase();break;case "lower":a=a.toLowerCase()}a="#"+a}else a=c(a);return a}function c(a){var b=p.getDict("css.keywordAliases");return a in b?b[a]:a}function a(a){return d.include(p.getArray("css.keywords"), +c(a))}function e(a){var b=h("utils"),a=b.trim(a);if(a.indexOf(":")==-1)return{name:a,value:u};a=a.split(":");return{name:b.trim(a.shift()),value:b.trim(a.join(":")).replace(/^(\$\{0\}|\$0)(\s*;?)$/,"${1}$2")}}function g(a,b){var c=r[b];c||(c=d.find(r,function(a){return a.prefix==b}));return c&&c.supports(a)}function j(a,b){var c=[];d.each(r,function(b,d){g(a,d)&&c.push(d)});!c.length&&!b&&d.each(r,function(a,b){a.obsolete||c.push(b)});return c}function l(a,b){d.isString(b)&&(b={prefix:b});r[a]=d.extend({}, +s,b)}function m(a,b){if(b){var c=p.get(b+"."+a);if(!d.isUndefined(c))return c}return p.get("css."+a)}function n(a,b,c){if(!d.isString(a))a=a.data;if(!i(a))return a;b&&(~a.indexOf(";")?a=a.split(";").join(" !important;"):a+=" !important");b=a.indexOf(":");a=a.substring(0,b).replace(/\s+$/,"")+m("valueSeparator",c)+h("utils").trim(a.substring(b+1));return a.replace(/\s*;\s*$/,m("propertyEnd",c))}function o(a){var b=p.getArray(a);d.each(p.getArray(a+"Addon"),function(a){a.charAt(0)=="-"?b=d.without(b, +a.substr(1)):(a.charAt(0)=="+"&&(a=a.substr(1)),b.push(a))});return b}var q=null,s={prefix:"emmet",obsolete:!1,transformName:function(a){return"-"+this.prefix+"-"+a},properties:function(){return o("css."+this.prefix+"Properties")||[]},supports:function(a){return d.include(this.properties(),a)}},r={},u="${1};",p=h("preferences");p.define("css.valueSeparator",": ","Defines a symbol that should be placed between CSS property and value when expanding CSS abbreviations.");p.define("css.propertyEnd",";", +"Defines a symbol that should be placed at the end of CSS property when expanding CSS abbreviations.");p.define("stylus.valueSeparator"," ","Defines a symbol that should be placed between CSS property and value when expanding CSS abbreviations in Stylus dialect.");p.define("stylus.propertyEnd","","Defines a symbol that should be placed at the end of CSS property when expanding CSS abbreviations in Stylus dialect.");p.define("sass.propertyEnd","","Defines a symbol that should be placed at the end of CSS property when expanding CSS abbreviations in SASS dialect."); +p.define("css.autoInsertVendorPrefixes",!0,"Automatically generate vendor-prefixed copies of expanded CSS property. By default, Emmet will generate vendor-prefixed properties only when you put dash before abbreviation (e.g. -bxsh). With this option enabled, you don\u2019t need dashes before abbreviations: Emmet will produce vendor-prefixed properties for you.");var v=d.template("A comma-separated list of CSS properties that may have <%= vendor %> vendor prefix. This list is used to generate a list of prefixed properties when expanding -property abbreviations. Empty list means that all possible CSS values may have <%= vendor %> prefix."), +w=d.template("A comma-separated list of additional CSS properties for css.<%= vendor %>Preperties preference. You should use this list if you want to add or remove a few CSS properties to original set. To add a new property, simply write its name, to remove it, precede property with hyphen.
For example, to add foo property and remove border-radius one, the preference value will look like this: foo, -border-radius.");d.each({webkit:"animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow, box-sizing, border-radius", +moz:"animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow, background-clip, border-radius", +ms:"accelerator, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, word-wrap, wrap-flow, wrap-margin, wrap-through, writing-mode", o:"dashboard-region, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, border-image, link, link-source, object-fit, object-position, tab-size, table-baseline, transform, transform-origin, transition, transition-delay, transition-duration, transition-property, transition-timing-function, accesskey, input-format, input-required, marquee-dir, marquee-loop, marquee-speed, marquee-style"}, -function(a,b){q.define("css."+b+"Properties",a,r({vendor:b}))});q.define("css.unitlessProperties","z-index, line-height, opacity, font-weight","The list of properties whose values \u200b\u200bmust not contain units.");d("w",{prefix:"webkit",supports:q.getArray("css.webkitProperties")});d("m",{prefix:"moz",supports:q.getArray("css.mozProperties")});d("s",{prefix:"ms",supports:q.getArray("css.msProperties")});d("o",{prefix:"o",supports:q.getArray("css.oProperties")});var t=q.getArray("css.unitlessProperties"), -x=["css","less","sass","scss"];h("resources").addResolver(function(a,b){return e.include(x,b)&&a.isElement()?j.expandToSnippet(a.abbreviation):null});var u=h("expandAbbreviation");u.addHandler(function(a,b,c){if(!e.include(x,b))return!1;var d=a.getSelectionRange().end,f=u.findAbbreviation(a);return f&&(b=emmet.expandAbbreviation(f,b,c))?(f=d-f.length,c=d,a.getContent().charAt(d)==";"&&c++,a.replaceContent(b,f,c),!0):!1});return j={addPrefix:d,supportsPrefix:b,prefixed:function(a,c){return b(a,c)? -"-"+c+"-"+a:a},listPrefixes:function(){return e.map(n,function(a){return a.prefix})},getPrefix:function(a){return n[a]},removePrefix:function(a){a in n&&delete n[a]},addUnitAlias:function(a,b){m[a]=b},getUnitAlias:function(a){return m[a]},removeUnitAlias:function(a){a in m&&delete m[a]},extractPrefixes:function(a){if(a.charAt(0)!="-")return{property:a,prefixes:null};for(var b=1,c=a.length,d,e=[];b/,">"+d)}function f(b,a,c){var i=h("abbreviationUtils");e.each(b.children,function(b){i.isBlock(b)&& -g(b,a,c);f(b,a,c)});return b}var c=h("preferences");c.define("filter.commentAfter",'\n<\!-- /<%= attr("id", "#") %><%= attr("class", ".") %> --\>',"A definition of comment that should be placed after matched element when comment filter is applied. This definition is an ERB-style template passed to _.template() function (see Underscore.js docs for details). In template context, the following properties and functions are availabe:\n
  • attr(name, before, after) \u2013 a function that outputsspecified attribute value concatenated with before and after strings. If attribute doesn't exists, the empty string will be returned.
  • node \u2013 current node (instance of AbbreviationNode)
  • name \u2013 name of current tag
  • padding \u2013 current string padding, can be used for formatting
"); -c.define("filter.commentBefore","","A definition of comment that should be placed before matched element when comment filter is applied. For more info, read description of filter.commentAfter property");c.define("filter.commentTrigger","id, class","A comma-separated list of attribute names that should exist in abbreviatoin where comment should be added. If you wish to add comment for every element, set this option to *");h("filters").add("c",function(b){var a= -e.template(c.get("filter.commentBefore")),d=e.template(c.get("filter.commentAfter"));return f(b,a,d)})});emmet.exec(function(h,e){function g(c){return c.replace(/([<>&])/g,function(b,a){return f[a]})}var f={"<":"<",">":">","&":"&"};h("filters").add("e",function b(a){e.each(a.children,function(a){a.start=g(a.start);a.end=g(a.end);a.content=g(a.content);b(a)});return a})}); -emmet.exec(function(h,e){function g(){return h("resources").getVariable("indentation")}function f(b){return b.parent&&!b.parent.parent&&!b.index()}function c(b,a){var c=h("abbreviationUtils");if(a.tag_nl===!0||c.isBlock(b))return!0;if(!b.parent||!a.inline_break)return!1;var f=0;return!!e.find(b.parent.children,function(b){b.isTextNode()||!c.isInline(b)?f=0:c.isInline(b)&&f++;if(f>=a.inline_break)return!0})}h("filters").add("_format",function a(d,i,j){var j=j||0,l=h("abbreviationUtils");e.each(d.children, -function(d){if(l.isSnippet(d)){if(!f(d))d.start=h("utils").getNewline()+d.start}else{d.start=d.end="%s";var e=h("utils"),o=h("abbreviationUtils"),q=o.isUnary(d),e=e.getNewline();if(i.tag_nl!==!1){var r=i.tag_nl===!0&&(i.tag_nl_leaf||d.children.length);if(!d.isTextNode()){if(c(d,i)){if(!f(d)&&(!o.isSnippet(d.parent)||d.index()))d.start=e+d.start;if(o.hasBlockChildren(d)||d.children.length&&c(d.children[0],i)||r&&!q)d.end=e+d.end;if(o.hasTagsInContent(d)||r&&!d.children.length&&!q)d.start+=e+g()}else if(o.isInline(d)&& -d.parent&&h("abbreviationUtils").hasBlockChildren(d.parent)&&!f(d))d.start=e+d.start;else if(o.isInline(d)&&o.hasBlockChildren(d))d.end=e+d.end;d.padding=g()}}}a(d,i,j+1)});return d})}); -emmet.exec(function(h,e){function g(f,c){var b="",a=[],d=c.attributeQuote(),g=c.cursor();e.each(f.attributeList(),function(e){var f=c.attributeName(e.name);switch(f.toLowerCase()){case "id":b+="#"+(e.value||g);break;case "class":b+="."+h("utils").trim(e.value||g).replace(/\s+/g,".");break;default:a.push(":"+f+" => "+d+(e.value||g)+d)}});a.length&&(b+="{"+a.join(", ")+"}");return b}h("filters").add("haml",function c(b,a,d){var d=d||0,i=h("abbreviationUtils");d||(b=h("filters").apply(b,"_format",a)); -e.each(b.children,function(b){if(!i.isSnippet(b)&&b.parent){var e=h("abbreviationUtils"),n=h("utils"),m=g(b,a),o=a.cursor(),e=e.isUnary(b),q=a.self_closing_tag&&e?"/":"",r="",r="%"+a.tagName(b.name());r.toLowerCase()=="%div"&&m&&m.indexOf("{")==-1&&(r="");b.end="";b.start=n.replaceSubstring(b.start,r+m+q+" ",b.start.indexOf("%s"),"%s");!b.children.length&&!e&&(b.start+=o)}c(b,a,d+1)});return b})}); -emmet.exec(function(h,e){function g(f,c){var b=c.attributeQuote(),a=c.cursor();return e.map(f.attributeList(),function(d){return" "+c.attributeName(d.name)+"="+b+(d.value||a)+b}).join("")}h("filters").add("html",function c(b,a,d){var d=d||0,i=h("abbreviationUtils");d||(b=h("filters").apply(b,"_format",a));e.each(b.children,function(b){if(!i.isSnippet(b)&&b.parent){var e=h("abbreviationUtils"),n=h("utils"),m=g(b,a),o=a.cursor(),e=e.isUnary(b),q="",r="";if(!b.isTextNode()){var t=a.tagName(b.name()); -e?(q="<"+t+m+a.selfClosing()+">",b.end=""):(q="<"+t+m+">",r="")}b.start=n.replaceSubstring(b.start,q,b.start.indexOf("%s"),"%s");b.end=n.replaceSubstring(b.end,r,b.end.indexOf("%s"),"%s");!b.children.length&&!e&&b.content.indexOf(o)==-1&&(b.start+=o)}c(b,a,d+1)});return b})}); -emmet.exec(function(h,e){var g=/^\s+/,f=/[\n\r]/g;h("filters").add("s",function b(a){var d=h("abbreviationUtils");e.each(a.children,function(a){if(!d.isSnippet(a))a.start=a.start.replace(g,""),a.end=a.end.replace(g,"");a.start=a.start.replace(f,"");a.end=a.end.replace(f,"");a.content=a.content.replace(f,"");b(a)});return a})}); -emmet.exec(function(h,e){function g(f,c){e.each(f.children,function(b){if(b.content)b.content=b.content.replace(c,"");g(b,c)});return f}h("preferences").define("filter.trimRegexp","[\\s|\\u00a0]*[\\d|#|\\-|*|\\u2022]+\\.?\\s*","Regular expression used to remove list markers (numbers, dashes, bullets, etc.) in t (trim) filter. The trim filter is useful for wrapping with abbreviation lists, pased from other documents (for example, Word documents).");h("filters").add("t",function(e){var c= -RegExp(h("preferences").get("filter.trimRegexp"));return g(e,c)})});emmet.exec(function(h,e){var g={"xsl:variable":1,"xsl:with-param":1};h("filters").add("xsl",function c(b){var a=h("abbreviationUtils");e.each(b.children,function(b){if(!a.isSnippet(b)&&(b.name()||"").toLowerCase()in g&&b.children.length)b.start=b.start.replace(/\s+select\s*=\s*(['"]).*?\1/,"");c(b)});return b})}); -emmet.exec(function(h,e){function g(a,b){return Math.round(Math.random()*(b-a)+a)}function f(a,b){for(var c=a.length,d=Math.min(c,b),f=[];f.length3&&b<=6?g(0,1):b>6&&b<=12?g(0,2):g(1,4);e.each(f(e.range(c)),function(b){a[b]+= -","})}h("abbreviationParser").addPreprocessor(function(e){var h=/^(?:lorem|lipsum)(\d*)$/i,l;e.findAll(function(e){if(e._name&&(l=e._name.match(h))){var i=l[1]||30;e._name="";e.data("forceNameResolving",e.isRepeating()||e.attributeList().length);e.data("paste",function(e){var h=[],j=0;e||(e=a.slice(0,i+1),e.length>5&&(e[4]+=","),j+=e.length,h.push(c(e,".")));for(;jalias:keyword_name.");p.define("css.unitAliases","e:em, p:%, x:ex, r:rem","A comma-separated list of unit aliases, used in CSS abbreviation. Each alias should be defined as alias:unit_value.");p.define("css.color.short",!0,"Should color values like #ffffff be shortened to #fff after abbreviation with color was expanded."); +p.define("css.color.case","keep","Letter case of color values generated by abbreviations with color (like c#0). Possible values are upper, lower and keep.");p.define("css.fuzzySearch",!0,"Enable fuzzy search among CSS snippet names. When enabled, every unknown snippet will be scored against available snippet names (not values or CSS properties!). The match with best score will be used to resolve snippet value. For example, with this preference enabled, the following abbreviations are equal: ov:h == ov-h == o-h == oh"); +p.define("css.fuzzySearchMinScore",0.3,"The minium score (from 0 to 1) that fuzzy-matched abbreviation should achive. Lower values may produce many false-positive matches, higher values may reduce possible matches.");l("w",{prefix:"webkit"});l("m",{prefix:"moz"});l("s",{prefix:"ms"});l("o",{prefix:"o"});var k=["css","less","sass","scss","stylus"];h("resources").addResolver(function(a,b){return d.include(k,b)&&a.isElement()?q.expandToSnippet(a.abbreviation,b):null});var t=h("expandAbbreviation");t.addHandler(function(a, +b,c){if(!d.include(k,b))return!1;var e=a.getSelectionRange().end,g=t.findAbbreviation(a);return g&&(b=emmet.expandAbbreviation(g,b,c))?(g=e-g.length,c=e,a.getContent().charAt(e)==";"&&b.charAt(b.length-1)==";"&&c++,a.replaceContent(b,g,c),!0):!1});return q={addPrefix:l,supportsPrefix:g,prefixed:function(a,b){return g(a,b)?"-"+b+"-"+a:a},listPrefixes:function(){return d.map(r,function(a){return a.prefix})},getPrefix:function(a){return r[a]},removePrefix:function(a){a in r&&delete r[a]},extractPrefixes:function(a){if(a.charAt(0)!= +"-")return{property:a,prefixes:null};for(var b=1,c=a.length,d,e=[];bbackground-color property with gradient first color as fallback for old browsers."); +h("expandAbbreviation").addHandler(function(a,b,c){var e=h("editorUtils").outputInfo(a,b,c);if(!d.include(s,e.syntax))return!1;var c=a.getCaretPos(),g=e.content,e=n(g,c);if(e.property){var f=l(e.property);if(f){var b=e.rule.options.offset||0,i=b+e.rule.toString().length;if(/[\n\r]/.test(e.property.value())){var o=e.property.valueRange(!0).start+f.valueRange.end,g=h("utils").replaceSubstring(g,";",o),c=n(g,c);c.property&&(f=l(c.property),e=c)}e.property.end(";");j(e.property,f.gradient,f.valueRange); +a.replaceContent(e.rule.toString(),b,i,!0);return!0}}return m(a,b)});h("reflectCSSValue").addHandler(function(a){var b=h("utils"),c=l(a);if(!c)return!1;var g=a.value(),f=function(a){return b.replaceSubstring(g,a,c.valueRange)};d.each(a.parent.getAll(e(a.name())),function(b){if(b!==a){var d=b.value().match(/^\s*(\-([a-z]+)\-)?linear\-gradient/);d?b.value(f(q.toString(c.gradient,d[2]||""))):b.value().match(/\s*\-webkit\-gradient/)&&b.value(f(q.oldWebkitLinearGradient(c.gradient)))}});return!0});return q= +{parse:function(a){var b=null;h("utils").trim(a).replace(/^([\w\-]+)\((.+?)\)$/,function(a,c,e){c=c.toLowerCase().replace(/^\-[a-z]+\-/,"");if(c=="linear-gradient"||c=="lg"){for(var a=o[0],e=h("stringStream").create(h("utils").trim(e)),c=[],g;g=e.next();)e.peek()==","?(c.push(e.current()),e.next(),e.eatSpace(),e.start=e.pos):g=="("&&e.skipTo(")");c.push(e.current());c=d.compact(d.map(c,f));if(c.length){if(r.test(c[0])||u.test(c[0]))a=c.shift();b={type:"linear",direction:a,colorStops:d.map(c,i)}}else b= +null;return""}return a});return b},oldWebkitLinearGradient:function(c){d.isString(c)&&(c=this.parse(c));if(!c)return null;var e=d.map(c.colorStops,d.clone);d.each(e,function(a){if("position"in a)if(~a.position.indexOf(".")||a.unit=="%")a.position=parseFloat(a.position)/(a.unit=="%"?100:1);else throw"Can't convert color stop '"+(a.position+(a.unit||""))+"'";});b(e);e=d.map(e,function(a,b){return!a.position&&!b?"from("+a.color+")":a.position==1&&b==e.length-1?"to("+a.color+")":"color-stop("+a.position.toFixed(2).replace(/\.?0+$/, +"")+", "+a.color+")"});return"-webkit-gradient(linear, "+a(c.direction)+", "+e.join(", ")+")"},toString:function(a,b){if(a.type=="linear"){var c=(b?"-"+b+"-":"")+"linear-gradient",e=d.map(a.colorStops,function(a){return a.color+("position"in a?" "+a.position+(a.unit||""):"")});a.direction&&(!p.get("css.gradient.omitDefaultDirection")||!d.include(o,a.direction))&&e.unshift(a.direction);return c+"("+e.join(", ")+")"}}}}); +emmet.exec(function(h,d){var f=h("handlerList").create(),i=h("resources");d.extend(i,{addGenerator:function(b,c,a){d.isString(b)&&(b=RegExp(b));f.add(function(a,d){var f;return(f=b.exec(a.name()))?c(f,a,d):null},a)}});i.addResolver(function(b,c){return f.exec(null,d.toArray(arguments))})}); +emmet.define("tagName",function(h,d){var f={empty:"area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command".split(","),blockLevel:"address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6".split(","),inlineLevel:"a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var".split(",")}, +i={p:"span",ul:"li",ol:"li",table:"tr",tr:"td",tbody:"tr",thead:"tr",tfoot:"tr",colgroup:"col",select:"option",optgroup:"option",audio:"source",video:"source",object:"param",map:"area"};return{resolve:function(b){b=(b||"").toLowerCase();return b in i?this.getMapping(b):this.isInlineLevel(b)?"span":"div"},getMapping:function(b){return i[b.toLowerCase()]},isInlineLevel:function(b){return this.isTypeOf(b,"inlineLevel")},isBlockLevel:function(b){return this.isTypeOf(b,"blockLevel")},isEmptyElement:function(b){return this.isTypeOf(b, +"empty")},isTypeOf:function(b,c){return d.include(f[c],b)},addMapping:function(b,c){i[b]=c},removeMapping:function(b){b in i&&delete i[b]},addElementToCollection:function(b,c){f[c]||(f[c]=[]);var a=this.getCollection(c);d.include(a,b)||a.push(b)},removeElementFromCollection:function(b,c){c in f&&(f[c]=d.without(this.getCollection(c),b))},getCollection:function(b){return f[b]}}}); +emmet.exec(function(h,d){function f(){return{element:e.get("bem.elementSeparator"),modifier:e.get("bem.modifierSeparator")}}function i(a){if(h("abbreviationUtils").isSnippet(a))return a;a.__bem={block:"",element:"",modifier:""};var e=b(a.attribute("class")).split(" "),g=/^[a-z]\-/i;a.__bem.block=d.find(e,function(a){return g.test(a)});if(!a.__bem.block)g=/^[a-z]/i,a.__bem.block=d.find(e,function(a){return g.test(a)})||"";(e=d.chain(e).map(function(b){var d;d=c(b,a,"element");d=c(d,a,"modifier");var e= +"",g="",h="",b=f();~d.indexOf(b.element)?(e=d.split(b.element),h=e[1].split(b.modifier),e=e[0],g=h.shift(),h=h.join(b.modifier)):~d.indexOf(b.modifier)&&(h=d.split(b.modifier),e=h.shift(),h=h.join(b.modifier));if(e||g||h){if(!e)e=a.__bem.block;d=e;var i=[];g&&(d+=b.element+g);i.push(d);h&&i.push(d+b.modifier+h);a.__bem.block=e;a.__bem.element=g;a.__bem.modifier=h;b=i}else b=d;return b}).flatten().uniq().value().join(" "))&&a.attribute("class",e);return a}function b(a){var b=h("utils"),a=(" "+(a|| +"")+" ").replace(/\s+/g," "),c=e.get("bem.shortElementPrefix");c&&(c=RegExp("\\s("+b.escapeForRegexp(c)+"+)","g"),a=a.replace(c,function(a,c){return" "+b.repeatString(f().element,c.length)}));return b.trim(a)}function c(a,b,c){var d=f(),e=RegExp("^("+d[c]+")+","g");if(e.test(a)){for(var g=0,e=a.replace(e,function(a){g=a.length/d[c].length;return""}),h=b;h.parent&&g--;)h=h.parent;if(!h||!h.__bem)h=b;if(h&&h.__bem)return a=h.__bem.block,c=="modifier"&&h.__bem.element&&(a+=d.element+h.__bem.element), +a+d[c]+e}return a}function a(b,c){b.name&&i(b,c);var e=h("abbreviationUtils");d.each(b.children,function(b){a(b,c);!e.isSnippet(b)&&b.start&&(g=!0)});return b}var e=h("preferences");e.define("bem.elementSeparator","__","Class name\u2019s element separator.");e.define("bem.modifierSeparator","_","Class name\u2019s modifier separator.");e.define("bem.shortElementPrefix","-","Symbol for describing short \u201cblock-element\u201d notation. Class names prefixed with this symbol will be treated as element name for parent\u2018s block name. Each symbol instance traverses one level up in parsed tree for block name lookup. Empty value will disable short notation."); +var g=!1;h("filters").add("bem",function(b,c){g=!1;b=a(b,c);g&&(b=h("filters").apply(b,"html",c));return b})}); +emmet.exec(function(h,d){function f(c,a,e){var g=h("utils"),f=b.get("filter.commentTrigger");if(f=="*"||d.find(f.split(","),function(a){return!!c.attribute(g.trim(a))}))f={node:c,name:c.name(),padding:c.parent?c.parent.padding:"",attr:function(a,b,d){return(a=c.attribute(a))?(b||"")+a+(d||""):""}},a=g.normalizeNewline(a?a(f):""),e=g.normalizeNewline(e?e(f):""),c.start=c.start.replace(//,">"+e)}function i(b,a,e){var g=h("abbreviationUtils");d.each(b.children,function(b){g.isBlock(b)&& +f(b,a,e);i(b,a,e)});return b}var b=h("preferences");b.define("filter.commentAfter",'\n<\!-- /<%= attr("id", "#") %><%= attr("class", ".") %> --\>',"A definition of comment that should be placed after matched element when comment filter is applied. This definition is an ERB-style template passed to _.template() function (see Underscore.js docs for details). In template context, the following properties and functions are availabe:\n
  • attr(name, before, after) \u2013 a function that outputsspecified attribute value concatenated with before and after strings. If attribute doesn't exists, the empty string will be returned.
  • node \u2013 current node (instance of AbbreviationNode)
  • name \u2013 name of current tag
  • padding \u2013 current string padding, can be used for formatting
"); +b.define("filter.commentBefore","","A definition of comment that should be placed before matched element when comment filter is applied. For more info, read description of filter.commentAfter property");b.define("filter.commentTrigger","id, class","A comma-separated list of attribute names that should exist in abbreviatoin where comment should be added. If you wish to add comment for every element, set this option to *");h("filters").add("c",function(c){var a= +d.template(b.get("filter.commentBefore")),e=d.template(b.get("filter.commentAfter"));return i(c,a,e)})});emmet.exec(function(h,d){function f(b){return b.replace(/([<>&])/g,function(b,a){return i[a]})}var i={"<":"<",">":">","&":"&"};h("filters").add("e",function c(a){d.each(a.children,function(a){a.start=f(a.start);a.end=f(a.end);a.content=f(a.content);c(a)});return a})}); +emmet.exec(function(h,d){function f(){return h("resources").getVariable("indentation")}function i(a){return a.parent&&!a.parent.parent&&!a.index()}function b(a,b){var d=h("abbreviationUtils");return b.tag_nl===!0||d.isBlock(a)?!0:!a.parent||!b.inline_break?!1:c(a.parent,b)}function c(a,b){var c=0,f=h("abbreviationUtils");return!!d.find(a.children,function(a){a.isTextNode()||!f.isInline(a)?c=0:f.isInline(a)&&c++;if(c>=b.inline_break)return!0})}function a(a,b){var f=h("abbreviationUtils");return!d.any(a.children, +function(a){return f.isSnippet(a)?!1:!f.isInline(a)})?c(a,b):!0}h("filters").add("_format",function g(c,l,m){var m=m||0,n=h("abbreviationUtils");d.each(c.children,function(c){if(n.isSnippet(c)){if(c.start=c.end="",!i(c)&&l.tag_nl!==!1&&b(c,l)&&!h("abbreviationUtils").isInline(c.parent))c.start=h("utils").getNewline()+c.start}else{c.start=c.end="%s";var d=h("utils"),j=h("abbreviationUtils"),r=j.isUnary(c),d=d.getNewline();if(l.tag_nl!==!1){var u=l.tag_nl===!0&&(l.tag_nl_leaf||c.children.length);if(!c.isTextNode()){if(b(c, +l)){if(!i(c)&&(!j.isSnippet(c.parent)||c.index()))c.start=d+c.start;if(j.hasBlockChildren(c)||c.children.length&&b(c.children[0],l)||u&&!r)c.end=d+c.end;if(j.hasTagsInContent(c)||u&&!c.children.length&&!r)c.start+=d+f()}else if(j.isInline(c)&&c.parent&&h("abbreviationUtils").hasBlockChildren(c.parent)&&!i(c))c.start=d+c.start;else if(j.isInline(c)&&a(c,l))c.end=d+c.end;c.padding=f()}}}g(c,l,m+1)});return c})}); +emmet.exec(function(h,d){function f(f,b){var c="",a=[],e=b.attributeQuote(),g=b.cursor();d.each(f.attributeList(),function(d){var f=b.attributeName(d.name);switch(f.toLowerCase()){case "id":c+="#"+(d.value||g);break;case "class":c+="."+h("utils").trim(d.value||g).replace(/\s+/g,".");break;default:a.push(":"+f+" => "+e+(d.value||g)+e)}});a.length&&(c+="{"+a.join(", ")+"}");return c}h("filters").add("haml",function b(c,a,e){var e=e||0,g=h("abbreviationUtils");e||(c=h("filters").apply(c,"_format",a)); +d.each(c.children,function(c){if(!g.isSnippet(c)&&c.parent){var d=h("abbreviationUtils"),m=h("utils"),n=f(c,a),o=a.cursor(),d=d.isUnary(c),q=a.self_closing_tag&&d?"/":"",s="",s="%"+a.tagName(c.name());s.toLowerCase()=="%div"&&n&&n.indexOf("{")==-1&&(s="");c.end="";c.start=m.replaceSubstring(c.start,s+n+q+" ",c.start.indexOf("%s"),"%s");!c.children.length&&!d&&(c.start+=o)}b(c,a,e+1)});return c})}); +emmet.exec(function(h,d){function f(f,b){var c=b.attributeQuote(),a=b.cursor();return d.map(f.attributeList(),function(d){return" "+b.attributeName(d.name)+"="+c+(d.value||a)+c}).join("")}h("filters").add("html",function b(c,a,e){var e=e||0,g=h("abbreviationUtils");e||(c=h("filters").apply(c,"_format",a));d.each(c.children,function(c){if(!g.isSnippet(c)&&c.parent){var d=h("abbreviationUtils"),m=h("utils"),n=f(c,a),o=a.cursor(),d=d.isUnary(c),q="",s="";if(!c.isTextNode()){var r=a.tagName(c.name()); +d?(q="<"+r+n+a.selfClosing()+">",c.end=""):(q="<"+r+n+">",s="")}c.start=m.replaceSubstring(c.start,q,c.start.indexOf("%s"),"%s");c.end=m.replaceSubstring(c.end,s,c.end.indexOf("%s"),"%s");!c.children.length&&!d&&c.content.indexOf(o)==-1&&(c.start+=o)}b(c,a,e+1)});return c})}); +emmet.exec(function(h,d){var f=/^\s+/,i=/[\n\r]/g;h("filters").add("s",function c(a){var e=h("abbreviationUtils");d.each(a.children,function(a){if(!e.isSnippet(a))a.start=a.start.replace(f,""),a.end=a.end.replace(f,"");a.start=a.start.replace(i,"");a.end=a.end.replace(i,"");a.content=a.content.replace(i,"");c(a)});return a})}); +emmet.exec(function(h,d){function f(h,b){d.each(h.children,function(c){if(c.content)c.content=c.content.replace(b,"");f(c,b)});return h}h("preferences").define("filter.trimRegexp","[\\s|\\u00a0]*[\\d|#|\\-|*|\\u2022]+\\.?\\s*","Regular expression used to remove list markers (numbers, dashes, bullets, etc.) in t (trim) filter. The trim filter is useful for wrapping with abbreviation lists, pased from other documents (for example, Word documents).");h("filters").add("t",function(d){var b= +RegExp(h("preferences").get("filter.trimRegexp"));return f(d,b)})});emmet.exec(function(h,d){var f={"xsl:variable":1,"xsl:with-param":1};h("filters").add("xsl",function b(c){var a=h("abbreviationUtils");d.each(c.children,function(c){if(!a.isSnippet(c)&&(c.name()||"").toLowerCase()in f&&c.children.length)c.start=c.start.replace(/\s+select\s*=\s*(['"]).*?\1/,"");b(c)});return c})}); +emmet.exec(function(h,d){function f(a,b){return Math.round(Math.random()*(b-a)+a)}function i(a,b){for(var c=a.length,e=Math.min(c,b),h=[];h.length3&&b<=6?f(0,1):b>6&&b<=12?f(0,2):f(1,4);d.each(i(d.range(c)),function(b){a[b]+= +","})}h("abbreviationParser").addPreprocessor(function(d){var h=/^(?:lorem|lipsum)(\d*)$/i,l;d.findAll(function(d){if(d._name&&(l=d._name.match(h))){var g=l[1]||30;d._name="";d.data("forceNameResolving",d.isRepeating()||d.attributeList().length);d.data("pasteOverwrites",!0);d.data("paste",function(d){var h=g,j=!d,d=[],l=0,h=parseInt(h,10);j&&(j=a.slice(0,h),j.length>5&&(j[4]+=","),l+=j.length,d.push(b(j,".")));for(;l\n\t${child}|\n","cc:ie":"<\!--[if IE]>\n\t${child}|\n","cc:noie":"<\!--[if !IE]><\!--\>\n\t${child}|\n<\!--","html:4t":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n', -"html:4s":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n',"html:xt":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n', -"html:xs":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n',"html:xxs":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n', -"html:5":'\n\n\n\t\n\t\n\n\n\t${child}|\n\n'},abbreviations:{a:'',"a:link":'',"a:mail":'',abbr:'',acronym:'',base:'',bdo:'',"bdo:r":'',"bdo:l":'',link:'',"link:css":'', +"bds:r":"border-style:ridge;","bds:i":"border-style:inset;","bds:o":"border-style:outset;",bdw:"border-width:|;",bdt:"border-top:|;",bt:"border-top:|;","bdt+":"border-top:${1:1px} ${2:solid} ${3:#000};","bdt:n":"border-top:none;",bdtw:"border-top-width:|;",bdts:"border-top-style:|;","bdts:n":"border-top-style:none;",bdtc:"border-top-color:#${1:000};",bdr:"border-right:|;",br:"border-right:|;","bdr+":"border-right:${1:1px} ${2:solid} ${3:#000};","bdr:n":"border-right:none;",bdrw:"border-right-width:|;", +bdrs:"border-right-style:|;","bdrs:n":"border-right-style:none;",bdrc:"border-right-color:#${1:000};",bdb:"border-bottom:|;",bb:"border-bottom:|;","bdb+":"border-bottom:${1:1px} ${2:solid} ${3:#000};","bdb:n":"border-bottom:none;",bdbw:"border-bottom-width:|;",bdbs:"border-bottom-style:|;","bdbs:n":"border-bottom-style:none;",bdbc:"border-bottom-color:#${1:000};",bdl:"border-left:|;",bl:"border-left:|;","bdl+":"border-left:${1:1px} ${2:solid} ${3:#000};","bdl:n":"border-left:none;",bdlw:"border-left-width:|;", +bdls:"border-left-style:|;","bdls:n":"border-left-style:none;",bdlc:"border-left-color:#${1:000};",bdrs:"border-radius:|;",bdtrrs:"border-top-right-radius:|;",bdtlrs:"border-top-left-radius:|;",bdbrrs:"border-bottom-right-radius:|;",bdblrs:"border-bottom-left-radius:|;",bg:"background:|;","bg+":"background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};","bg:n":"background:none;","bg:ie":"filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');",bgc:"background-color:#${1:fff};", +bgi:"background-image:url(|);","bgi:n":"background-image:none;",bgr:"background-repeat:|;","bgr:n":"background-repeat:no-repeat;","bgr:x":"background-repeat:repeat-x;","bgr:y":"background-repeat:repeat-y;",bga:"background-attachment:|;","bga:f":"background-attachment:fixed;","bga:s":"background-attachment:scroll;",bgp:"background-position:${1:0} ${2:0};",bgpx:"background-position-x:|;",bgpy:"background-position-y:|;",bgbk:"background-break:|;","bgbk:bb":"background-break:bounding-box;","bgbk:eb":"background-break:each-box;", +"bgbk:c":"background-break:continuous;",bgcp:"background-clip:|;","bgcp:bb":"background-clip:border-box;","bgcp:pb":"background-clip:padding-box;","bgcp:cb":"background-clip:content-box;","bgcp:nc":"background-clip:no-clip;",bgo:"background-origin:|;","bgo:pb":"background-origin:padding-box;","bgo:bb":"background-origin:border-box;","bgo:cb":"background-origin:content-box;",bgz:"background-size:|;","bgz:a":"background-size:auto;","bgz:ct":"background-size:contain;","bgz:cv":"background-size:cover;", +c:"color:#${1:000};",cm:"/* |${child} */",cn:"content:|;",tbl:"table-layout:|;","tbl:a":"table-layout:auto;","tbl:f":"table-layout:fixed;",cps:"caption-side:|;","cps:t":"caption-side:top;","cps:b":"caption-side:bottom;",ec:"empty-cells:|;","ec:s":"empty-cells:show;","ec:h":"empty-cells:hide;",lis:"list-style:|;","lis:n":"list-style:none;",lisp:"list-style-position:|;","lisp:i":"list-style-position:inside;","lisp:o":"list-style-position:outside;",list:"list-style-type:|;","list:n":"list-style-type:none;", +"list:d":"list-style-type:disc;","list:c":"list-style-type:circle;","list:s":"list-style-type:square;","list:dc":"list-style-type:decimal;","list:dclz":"list-style-type:decimal-leading-zero;","list:lr":"list-style-type:lower-roman;","list:ur":"list-style-type:upper-roman;",lisi:"list-style-image:|;","lisi:n":"list-style-image:none;",q:"quotes:|;","q:n":"quotes:none;","q:ru":"quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';","q:en":"quotes:'\\201C' '\\201D' '\\2018' '\\2019';",ct:"content:|;","ct:n":"content:normal;", +"ct:oq":"content:open-quote;","ct:noq":"content:no-open-quote;","ct:cq":"content:close-quote;","ct:ncq":"content:no-close-quote;","ct:a":"content:attr(|);","ct:c":"content:counter(|);","ct:cs":"content:counters(|);",coi:"counter-increment:|;",cor:"counter-reset:|;",va:"vertical-align:|;","va:sup":"vertical-align:super;","va:t":"vertical-align:top;","va:tt":"vertical-align:text-top;","va:m":"vertical-align:middle;","va:bl":"vertical-align:baseline;","va:b":"vertical-align:bottom;","va:tb":"vertical-align:text-bottom;", +"va:sub":"vertical-align:sub;",ta:"text-align:|;","ta:l":"text-align:left;","ta:c":"text-align:center;","ta:r":"text-align:right;",tal:"text-align-last:|;","tal:a":"text-align-last:auto;","tal:l":"text-align-last:left;","tal:c":"text-align-last:center;","tal:r":"text-align-last:right;",td:"text-decoration:|;","td:n":"text-decoration:none;","td:u":"text-decoration:underline;","td:o":"text-decoration:overline;","td:l":"text-decoration:line-through;",te:"text-emphasis:|;","te:n":"text-emphasis:none;", +"te:ac":"text-emphasis:accent;","te:dt":"text-emphasis:dot;","te:c":"text-emphasis:circle;","te:ds":"text-emphasis:disc;","te:b":"text-emphasis:before;","te:a":"text-emphasis:after;",th:"text-height:|;","th:a":"text-height:auto;","th:f":"text-height:font-size;","th:t":"text-height:text-size;","th:m":"text-height:max-size;",ti:"text-indent:|;","ti:-":"text-indent:-9999px;",tj:"text-justify:|;","tj:a":"text-justify:auto;","tj:iw":"text-justify:inter-word;","tj:ii":"text-justify:inter-ideograph;","tj:ic":"text-justify:inter-cluster;", +"tj:d":"text-justify:distribute;","tj:k":"text-justify:kashida;","tj:t":"text-justify:tibetan;",to:"text-outline:|;","to+":"text-outline:${1:0} ${2:0} ${3:#000};","to:n":"text-outline:none;",tr:"text-replace:|;","tr:n":"text-replace:none;",tt:"text-transform:|;","tt:n":"text-transform:none;","tt:c":"text-transform:capitalize;","tt:u":"text-transform:uppercase;","tt:l":"text-transform:lowercase;",tw:"text-wrap:|;","tw:n":"text-wrap:normal;","tw:no":"text-wrap:none;","tw:u":"text-wrap:unrestricted;", +"tw:s":"text-wrap:suppress;",tsh:"text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};","tsh+":"text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};","tsh:n":"text-shadow:none;",trf:"transform:|;","trf:skx":"transform: skewX(${1:angle});","trf:sky":"transform: skewY(${1:angle});","trf:sc":"transform: scale(${1:x}, ${2:y});","trf:scx":"transform: scaleX(${1:x});","trf:scy":"transform: scaleY(${1:y});","trf:r":"transform: rotate(${1:angle});","trf:t":"transform: translate(${1:x}, ${2:y});","trf:tx":"transform: translateX(${1:x});", +"trf:ty":"transform: translateY(${1:y});",trs:"transition:${1:prop} ${2:time};",trsde:"transition-delay:${1:time};",trsdu:"transition-duration:${1:time};",trsp:"transition-property:${1:prop};",trstf:"transition-timing-function:${1:tfunc};",lh:"line-height:|;",whs:"white-space:|;","whs:n":"white-space:normal;","whs:p":"white-space:pre;","whs:nw":"white-space:nowrap;","whs:pw":"white-space:pre-wrap;","whs:pl":"white-space:pre-line;",whsc:"white-space-collapse:|;","whsc:n":"white-space-collapse:normal;", +"whsc:k":"white-space-collapse:keep-all;","whsc:l":"white-space-collapse:loose;","whsc:bs":"white-space-collapse:break-strict;","whsc:ba":"white-space-collapse:break-all;",wob:"word-break:|;","wob:n":"word-break:normal;","wob:k":"word-break:keep-all;","wob:l":"word-break:loose;","wob:bs":"word-break:break-strict;","wob:ba":"word-break:break-all;",wos:"word-spacing:|;",wow:"word-wrap:|;","wow:nm":"word-wrap:normal;","wow:n":"word-wrap:none;","wow:u":"word-wrap:unrestricted;","wow:s":"word-wrap:suppress;", +lts:"letter-spacing:|;",f:"font:|;","f+":"font:${1:1em} ${2:Arial,sans-serif};",fw:"font-weight:|;","fw:n":"font-weight:normal;","fw:b":"font-weight:bold;","fw:br":"font-weight:bolder;","fw:lr":"font-weight:lighter;",fs:"font-style:|;","fs:n":"font-style:normal;","fs:i":"font-style:italic;","fs:o":"font-style:oblique;",fv:"font-variant:|;","fv:n":"font-variant:normal;","fv:sc":"font-variant:small-caps;",fz:"font-size:|;",fza:"font-size-adjust:|;","fza:n":"font-size-adjust:none;",ff:"font-family:|;", +"ff:s":"font-family:serif;","ff:ss":"font-family:sans-serif;","ff:c":"font-family:cursive;","ff:f":"font-family:fantasy;","ff:m":"font-family:monospace;",fef:"font-effect:|;","fef:n":"font-effect:none;","fef:eg":"font-effect:engrave;","fef:eb":"font-effect:emboss;","fef:o":"font-effect:outline;",fem:"font-emphasize:|;",femp:"font-emphasize-position:|;","femp:b":"font-emphasize-position:before;","femp:a":"font-emphasize-position:after;",fems:"font-emphasize-style:|;","fems:n":"font-emphasize-style:none;", +"fems:ac":"font-emphasize-style:accent;","fems:dt":"font-emphasize-style:dot;","fems:c":"font-emphasize-style:circle;","fems:ds":"font-emphasize-style:disc;",fsm:"font-smooth:|;","fsm:a":"font-smooth:auto;","fsm:n":"font-smooth:never;","fsm:aw":"font-smooth:always;",fst:"font-stretch:|;","fst:n":"font-stretch:normal;","fst:uc":"font-stretch:ultra-condensed;","fst:ec":"font-stretch:extra-condensed;","fst:c":"font-stretch:condensed;","fst:sc":"font-stretch:semi-condensed;","fst:se":"font-stretch:semi-expanded;", +"fst:e":"font-stretch:expanded;","fst:ee":"font-stretch:extra-expanded;","fst:ue":"font-stretch:ultra-expanded;",op:"opacity:|;","op:ie":"filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);","op:ms":"-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",rz:"resize:|;","rz:n":"resize:none;","rz:b":"resize:both;","rz:h":"resize:horizontal;","rz:v":"resize:vertical;",cur:"cursor:|;","cur:a":"cursor:auto;","cur:d":"cursor:default;","cur:c":"cursor:crosshair;","cur:ha":"cursor:hand;", +"cur:he":"cursor:help;","cur:m":"cursor:move;","cur:p":"cursor:pointer;","cur:t":"cursor:text;",pgbb:"page-break-before:|;","pgbb:au":"page-break-before:auto;","pgbb:al":"page-break-before:always;","pgbb:l":"page-break-before:left;","pgbb:r":"page-break-before:right;",pgbi:"page-break-inside:|;","pgbi:au":"page-break-inside:auto;","pgbi:av":"page-break-inside:avoid;",pgba:"page-break-after:|;","pgba:au":"page-break-after:auto;","pgba:al":"page-break-after:always;","pgba:l":"page-break-after:left;", +"pgba:r":"page-break-after:right;",orp:"orphans:|;",wid:"widows:|;"}},html:{filters:"html",profile:"html",snippets:{c:"<\!-- |${child} --\>","cc:ie6":"<\!--[if lte IE 6]>\n\t${child}|\n","cc:ie":"<\!--[if IE]>\n\t${child}|\n","cc:noie":"<\!--[if !IE]><\!--\>\n\t${child}|\n<\!--","html:4t":'\n\n\n\t\n\t${1:Document}\n\n\n\t${child}${2}\n\n', +"html:4s":'\n\n\n\t\n\t${1:Document}\n\n\n\t${child}${2}\n\n',"html:xt":'\n\n\n\t\n\t\n\n\n\t${child}${2}\n\n', +"html:xs":'\n\n\n\t\n\t${1:Document}\n\n\n\t${child}${2}\n\n',"html:xxs":'\n\n\n\t\n\t${1:Document}\n\n\n\t${child}${2}\n\n', +"html:5":'\n\n\n\t\n\t${1:Document}\n\n\n\t${child}${2}\n\n'},abbreviations:{"!":"html:5",a:'',"a:link":'',"a:mail":'',abbr:'',acronym:'',base:'',bdo:'',"bdo:r":'',"bdo:l":'',link:'',"link:css":'', "link:print":'',"link:favicon":'',"link:touch":'',"link:rss":'',"link:atom":'',"meta:utf":'', "meta:win":'',"meta:compat":'',style:" diff --git a/applications/admin/static/codemirror/mode/clike/scala.html b/applications/admin/static/codemirror/mode/clike/scala.html index a5aba99c..39115967 100644 --- a/applications/admin/static/codemirror/mode/clike/scala.html +++ b/applications/admin/static/codemirror/mode/clike/scala.html @@ -6,6 +6,7 @@ + @@ -65,14 +67,22 @@ StringStream.prototype = { -

JavaScript mode supports a single configuration - option, json, which will set the mode to expect JSON - data rather than a JavaScript program.

+

+ JavaScript mode supports a two configuration + options: +

+

-

MIME types defined: text/javascript, application/json.

+

MIME types defined: text/javascript, application/json, text/typescript, application/typescript.

diff --git a/applications/admin/static/codemirror/mode/javascript/javascript.js b/applications/admin/static/codemirror/mode/javascript/javascript.js index 6ece1bef..f00be91e 100644 --- a/applications/admin/static/codemirror/mode/javascript/javascript.js +++ b/applications/admin/static/codemirror/mode/javascript/javascript.js @@ -1,6 +1,9 @@ +// TODO actually recognize syntax of TypeScript constructs + CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var jsonMode = parserConfig.json; + var isTS = parserConfig.typescript; // Tokenizer @@ -8,7 +11,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - return { + + var jsKeywords = { "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), @@ -17,6 +21,35 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom }; + + // Extend the 'normal' keywords with the TypeScript language extensions + if (isTS) { + var type = {type: "variable", style: "variable-3"}; + var tsKeywords = { + // object-like things + "interface": kw("interface"), + "class": kw("class"), + "extends": kw("extends"), + "constructor": kw("constructor"), + + // scope modifiers + "public": kw("public"), + "private": kw("private"), + "protected": kw("protected"), + "static": kw("static"), + + "super": kw("super"), + + // types + "string": type, "number": type, "bool": type, "any": type + }; + + for (var attr in tsKeywords) { + jsKeywords[attr] = tsKeywords[attr]; + } + } + + return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|]/; @@ -66,7 +99,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { stream.skipToEnd(); return ret("comment", "comment"); } - else if (state.reAllowed) { + else if (state.lastType == "operator" || state.lastType == "keyword c" || + /^[\[{}\(,;:]$/.test(state.lastType)) { nextUntilUnescaped(stream, "/"); stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla return ret("regexp", "string-2"); @@ -87,7 +121,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { else { stream.eatWhile(/[\w\$_]/); var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.kwAllowed) ? ret(known.type, known.style, word) : + return (known && state.lastType != ".") ? ret(known.type, known.style, word) : ret("variable", "variable", word); } } @@ -175,8 +209,8 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { var defaultVars = {name: "this", next: {name: "arguments"}}; function pushcontext() { - if (!cx.state.context) cx.state.localVars = defaultVars; cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; + cx.state.localVars = defaultVars; } function popcontext() { cx.state.localVars = cx.state.context.vars; @@ -275,21 +309,32 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "}") return cont(); return pass(statement, block); } + function maybetype(type) { + if (type == ":") return cont(typedef); + return pass(); + } + function typedef(type) { + if (type == "variable"){cx.marked = "variable-3"; return cont();} + return pass(); + } function vardef1(type, value) { - if (type == "variable"){register(value); return cont(vardef2);} - return cont(); + if (type == "variable") { + register(value); + return isTS ? cont(maybetype, vardef2) : cont(vardef2); + } + return pass(); } function vardef2(type, value) { if (value == "=") return cont(expression, vardef2); if (type == ",") return cont(vardef1); } function forspec1(type) { - if (type == "var") return cont(vardef1, forspec2); - if (type == ";") return pass(forspec2); + if (type == "var") return cont(vardef1, expect(";"), forspec2); + if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybein); - return pass(forspec2); + return cont(forspec2); } - function formaybein(type, value) { + function formaybein(_type, value) { if (value == "in") return cont(expression); return cont(maybeoperator, forspec2); } @@ -306,7 +351,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); } function funarg(type, value) { - if (type == "variable") {register(value); return cont();} + if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} } // Interface @@ -315,8 +360,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { startState: function(basecolumn) { return { tokenize: jsTokenBase, - reAllowed: true, - kwAllowed: true, + lastType: null, cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, @@ -334,28 +378,34 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) { if (stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; - state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/)); - state.kwAllowed = type != '.'; + state.lastType = type; return parseJS(state, style, type, content, stream); }, indent: function(state, textAfter) { + if (state.tokenize == jsTokenComment) return CodeMirror.Pass; if (state.tokenize != jsTokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; - if (type == "vardef") return lexical.indented + 4; + if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "stat" || type == "form") return lexical.indented + indentUnit; + else if (type == "form") return lexical.indented + indentUnit; + else if (type == "stat") + return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? indentUnit : 0); else if (lexical.info == "switch" && !closing) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); else return lexical.indented + (closing ? 0 : indentUnit); }, - electricChars: ":{}" + electricChars: ":{}", + + jsonMode: jsonMode }; }); CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); +CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); +CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/applications/admin/static/codemirror/mode/less/index.html b/applications/admin/static/codemirror/mode/less/index.html index 69467bbb..7f27cf30 100644 --- a/applications/admin/static/codemirror/mode/less/index.html +++ b/applications/admin/static/codemirror/mode/less/index.html @@ -5,8 +5,9 @@ CodeMirror: LESS mode + - + diff --git a/applications/admin/static/codemirror/mode/python/index.html b/applications/admin/static/codemirror/mode/python/index.html index 9f1164e2..7a26d278 100644 --- a/applications/admin/static/codemirror/mode/python/index.html +++ b/applications/admin/static/codemirror/mode/python/index.html @@ -5,6 +5,7 @@ CodeMirror: Python mode + diff --git a/applications/admin/static/codemirror/mode/python/python.js b/applications/admin/static/codemirror/mode/python/python.js index fc5b9551..a12e326d 100644 --- a/applications/admin/static/codemirror/mode/python/python.js +++ b/applications/admin/static/codemirror/mode/python/python.js @@ -160,7 +160,7 @@ CodeMirror.defineMode("python", function(conf, parserConf) { var singleline = delimiter.length == 1; var OUTCLASS = 'string'; - return function tokenString(stream, state) { + function tokenString(stream, state) { while (!stream.eol()) { stream.eatWhile(/[^'"\\]/); if (stream.eat('\\')) { @@ -183,7 +183,9 @@ CodeMirror.defineMode("python", function(conf, parserConf) { } } return OUTCLASS; - }; + } + tokenString.isString = true; + return tokenString; } function indent(stream, state, type) { @@ -323,9 +325,9 @@ CodeMirror.defineMode("python", function(conf, parserConf) { return style; }, - indent: function(state, textAfter) { + indent: function(state) { if (state.tokenize != tokenBase) { - return 0; + return state.tokenize.isString ? CodeMirror.Pass : 0; } return state.scopes[0].offset; diff --git a/applications/admin/static/codemirror/mode/xml/xml.js b/applications/admin/static/codemirror/mode/xml/xml.js index cd69f62f..7b11fd67 100644 --- a/applications/admin/static/codemirror/mode/xml/xml.js +++ b/applications/admin/static/codemirror/mode/xml/xml.js @@ -70,11 +70,12 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return "meta"; } else { - type = stream.eat("/") ? "closeTag" : "openTag"; - stream.eatSpace(); + var isClose = stream.eat("/"); tagName = ""; var c; while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + if (!tagName) return "error"; + type = isClose ? "closeTag" : "openTag"; state.tokenize = inTag; return "tag"; } @@ -114,7 +115,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { return state.tokenize(stream, state); } else { - stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/); + stream.eatWhile(/[^\s\u00a0=<>\"\']/); return "word"; } } @@ -210,14 +211,16 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { } function endtag(startOfLine) { return function(type) { + var tagName = curState.tagName; + curState.tagName = null; if (type == "selfcloseTag" || - (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase()))) { - maybePopContext(curState.tagName.toLowerCase()); + (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { + maybePopContext(tagName.toLowerCase()); return cont(); } if (type == "endTag") { - maybePopContext(curState.tagName.toLowerCase()); - pushContext(curState.tagName, startOfLine); + maybePopContext(tagName.toLowerCase()); + pushContext(tagName, startOfLine); return cont(); } return cont(); @@ -255,6 +258,7 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { function attribute(type) { if (type == "equals") return cont(attvalue, attributes); if (!Kludges.allowMissing) setStyle = "error"; + else if (type == "word") setStyle = "attribute"; return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); } function attvalue(type) { @@ -308,15 +312,9 @@ CodeMirror.defineMode("xml", function(config, parserConfig) { else return 0; }, - compareStates: function(a, b) { - if (a.indented != b.indented || a.tokenize != b.tokenize) return false; - for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) { - if (!ca || !cb) return ca == cb; - if (ca.tagName != cb.tagName || ca.indent != cb.indent) return false; - } - }, + electricChars: "/", - electricChars: "/" + configuration: parserConfig.htmlMode ? "html" : "xml" }; }); diff --git a/applications/admin/static/codemirror/theme/ambiance.css b/applications/admin/static/codemirror/theme/ambiance.css index ef5f8d09..beec5538 100644 --- a/applications/admin/static/codemirror/theme/ambiance.css +++ b/applications/admin/static/codemirror/theme/ambiance.css @@ -1,4 +1,4 @@ -/* ambiance theme for code-mirror */ +/* ambiance theme for codemirror */ /* Color scheme */ @@ -33,13 +33,13 @@ .cm-s-ambiance .CodeMirror-selected { background: rgba(255, 255, 255, 0.15); } -.CodeMirror-focused .cm-s-ambiance .CodeMirror-selected { +.cm-s-ambiance .CodeMirror-focused .CodeMirror-selected { background: rgba(255, 255, 255, 0.10); } /* Editor styling */ -.cm-s-ambiance { +.cm-s-ambiance.CodeMirror { line-height: 1.40em; font-family: Monaco, Menlo,"Andale Mono","lucida console","Courier New",monospace !important; color: #E6E1DC; @@ -50,21 +50,16 @@ box-shadow: inset 0 0 10px black; } -.cm-s-ambiance .CodeMirror-gutter { +.cm-s-ambiance .CodeMirror-gutters { background: #3D3D3D; - padding: 0 5px; - text-shadow: #333 1px 1px; border-right: 1px solid #4D4D4D; box-shadow: 0 10px 20px black; } -.cm-s-ambiance .CodeMirror-gutter .CodeMirror-gutter-text { +.cm-s-ambiance .CodeMirror-linenumber { text-shadow: 0px 1px 1px #4d4d4d; color: #222; -} - -.cm-s-ambiance .CodeMirror-lines { - + padding: 0 5px; } .cm-s-ambiance .CodeMirror-lines .CodeMirror-cursor { @@ -75,7 +70,7 @@ background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); } -.cm-s-ambiance, -.cm-s-ambiance .CodeMirror-gutter { +.cm-s-ambiance.CodeMirror, +.cm-s-ambiance .CodeMirror-gutters { background-image: url("data:image/png;base64,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"); } diff --git a/applications/admin/static/codemirror/theme/blackboard.css b/applications/admin/static/codemirror/theme/blackboard.css index 7b73a924..f2bde690 100644 --- a/applications/admin/static/codemirror/theme/blackboard.css +++ b/applications/admin/static/codemirror/theme/blackboard.css @@ -1,9 +1,9 @@ /* Port of TextMate's Blackboard theme */ -.cm-s-blackboard { background: #0C1021; color: #F8F8F8; } +.cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; } .cm-s-blackboard .CodeMirror-selected { background: #253B76 !important; } -.cm-s-blackboard .CodeMirror-gutter { background: #0C1021; border-right: 0; } -.cm-s-blackboard .CodeMirror-gutter-text { color: #888; } +.cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } +.cm-s-blackboard .CodeMirror-linenumber { color: #888; } .cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7 !important; } .cm-s-blackboard .cm-keyword { color: #FBDE2D; } diff --git a/applications/admin/static/codemirror/theme/cobalt.css b/applications/admin/static/codemirror/theme/cobalt.css index dbbb7e49..6095799f 100644 --- a/applications/admin/static/codemirror/theme/cobalt.css +++ b/applications/admin/static/codemirror/theme/cobalt.css @@ -1,7 +1,7 @@ -.cm-s-cobalt { background: #002240; color: white; } +.cm-s-cobalt.CodeMirror { background: #002240; color: white; } .cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-cobalt .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-cobalt .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-cobalt span.cm-comment { color: #08f; } diff --git a/applications/admin/static/codemirror/theme/erlang-dark.css b/applications/admin/static/codemirror/theme/erlang-dark.css index 486b1c47..ea9c26c4 100644 --- a/applications/admin/static/codemirror/theme/erlang-dark.css +++ b/applications/admin/static/codemirror/theme/erlang-dark.css @@ -1,7 +1,7 @@ -.cm-s-erlang-dark { background: #002240; color: white; } +.cm-s-erlang-dark.CodeMirror { background: #002240; color: white; } .cm-s-erlang-dark div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-erlang-dark .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-erlang-dark .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-erlang-dark span.cm-atom { color: #845dc4; } diff --git a/applications/admin/static/codemirror/theme/lesser-dark.css b/applications/admin/static/codemirror/theme/lesser-dark.css index ffa6a3f2..67f71ad7 100644 --- a/applications/admin/static/codemirror/theme/lesser-dark.css +++ b/applications/admin/static/codemirror/theme/lesser-dark.css @@ -9,15 +9,15 @@ Ported to CodeMirror by Peter Kroon font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', 'Monaco', Courier, monospace !important; } -.cm-s-lesser-dark { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } +.cm-s-lesser-dark.CodeMirror { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } .cm-s-lesser-dark div.CodeMirror-selected {background: #45443B !important;} /* 33322B*/ .cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white !important; } -.cm-s-lesser-dark .CodeMirror-lines { margin-left:3px; margin-right:3px; }/*editable code holder*/ +.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ div.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ -.cm-s-lesser-dark .CodeMirror-gutter { background: #262626; border-right:1px solid #aaa; padding-right:3px; min-width:2.5em; } -.cm-s-lesser-dark .CodeMirror-gutter-text { color: #777; } +.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; } +.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; } .cm-s-lesser-dark span.cm-keyword { color: #599eff; } .cm-s-lesser-dark span.cm-atom { color: #C2B470; } diff --git a/applications/admin/static/codemirror/theme/monokai.css b/applications/admin/static/codemirror/theme/monokai.css index f01d066f..a0b3c7c0 100644 --- a/applications/admin/static/codemirror/theme/monokai.css +++ b/applications/admin/static/codemirror/theme/monokai.css @@ -1,9 +1,9 @@ /* Based on Sublime Text's Monokai theme */ -.cm-s-monokai {background: #272822; color: #f8f8f2;} +.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2;} .cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} -.cm-s-monokai .CodeMirror-gutter {background: #272822; border-right: 0px;} -.cm-s-monokai .CodeMirror-gutter-text {color: #d0d0d0;} +.cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} +.cm-s-monokai .CodeMirror-linenumber {color: #d0d0d0;} .cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;} .cm-s-monokai span.cm-comment {color: #75715e;} diff --git a/applications/admin/static/codemirror/theme/night.css b/applications/admin/static/codemirror/theme/night.css index 9d51d950..8804a399 100644 --- a/applications/admin/static/codemirror/theme/night.css +++ b/applications/admin/static/codemirror/theme/night.css @@ -1,9 +1,9 @@ /* Loosely based on the Midnight Textmate theme */ -.cm-s-night { background: #0a001f; color: #f8f8f8; } +.cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; } .cm-s-night div.CodeMirror-selected { background: #447 !important; } -.cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; } +.cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-night .CodeMirror-linenumber { color: #f8f8f8; } .cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-night span.cm-comment { color: #6900a1; } diff --git a/applications/admin/static/codemirror/theme/rubyblue.css b/applications/admin/static/codemirror/theme/rubyblue.css index 502817ae..8817de07 100644 --- a/applications/admin/static/codemirror/theme/rubyblue.css +++ b/applications/admin/static/codemirror/theme/rubyblue.css @@ -1,9 +1,9 @@ .cm-s-rubyblue { font:13px/1.4em Trebuchet, Verdana, sans-serif; } /* - customized editor font - */ -.cm-s-rubyblue { background: #112435; color: white; } +.cm-s-rubyblue.CodeMirror { background: #112435; color: white; } .cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } -.cm-s-rubyblue .CodeMirror-gutter { background: #1F4661; border-right: 7px solid #3E7087; min-width:2.5em; } -.cm-s-rubyblue .CodeMirror-gutter-text { color: white; } +.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; } +.cm-s-rubyblue .CodeMirror-linenumber { color: white; } .cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; } diff --git a/applications/admin/static/codemirror/theme/vibrant-ink.css b/applications/admin/static/codemirror/theme/vibrant-ink.css index de5bc2c9..22024a48 100644 --- a/applications/admin/static/codemirror/theme/vibrant-ink.css +++ b/applications/admin/static/codemirror/theme/vibrant-ink.css @@ -1,10 +1,10 @@ /* Taken from the popular Visual Studio Vibrant Ink Schema */ -.cm-s-vibrant-ink { background: black; color: white; } +.cm-s-vibrant-ink.CodeMirror { background: black; color: white; } .cm-s-vibrant-ink .CodeMirror-selected { background: #35493c !important; } -.cm-s-vibrant-ink .CodeMirror-gutter { background: #002240; border-right: 1px solid #aaa; } -.cm-s-vibrant-ink .CodeMirror-gutter-text { color: #d0d0d0; } +.cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } +.cm-s-vibrant-ink .CodeMirror-linenumber { color: #d0d0d0; } .cm-s-vibrant-ink .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-vibrant-ink .cm-keyword { color: #CC7832; } diff --git a/applications/admin/static/codemirror/theme/web2py.css b/applications/admin/static/codemirror/theme/web2py.css index f3272aec..fad60b58 100644 --- a/applications/admin/static/codemirror/theme/web2py.css +++ b/applications/admin/static/codemirror/theme/web2py.css @@ -28,7 +28,7 @@ .cm-s-web2py { line-height: 1.40em; - font-family: Monaco, Menlo,"Andale Mono","lucida console","Courier New",monospace !important; + font-family: "serif"; } /* Fullscreen mode and active line highlight */ @@ -42,4 +42,4 @@ z-index: 9999; } -.cm-s-web2py .activeline {background: #e8f2ff !important;} \ No newline at end of file +.cm-s-web2py .activeline {background: #e8f2ff !important;} diff --git a/applications/admin/static/codemirror/theme/xq-dark.css b/applications/admin/static/codemirror/theme/xq-dark.css index 493e3a63..fd9bb12a 100644 --- a/applications/admin/static/codemirror/theme/xq-dark.css +++ b/applications/admin/static/codemirror/theme/xq-dark.css @@ -20,10 +20,10 @@ 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. */ -.cm-s-xq-dark { background: #0a001f; color: #f8f8f8; } +.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; } .cm-s-xq-dark span.CodeMirror-selected { background: #a8f !important; } -.cm-s-xq-dark .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-xq-dark .CodeMirror-gutter-text { color: #f8f8f8; } +.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } +.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } .cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white !important; } .cm-s-xq-dark span.cm-keyword {color: #FFBD40;} diff --git a/applications/admin/static/js/ajax_editor.js b/applications/admin/static/js/ajax_editor.js index 44d5de7d..6dcde09d 100644 --- a/applications/admin/static/js/ajax_editor.js +++ b/applications/admin/static/js/ajax_editor.js @@ -44,7 +44,7 @@ function doHighlight(highlight) { window.ace_editor.gotoLine(highlight.lineno); } else if (window.mirror) { window.mirror.setSelection({line:highlight.lineno,ch:0}, - {line:highlight.end,ch:0}); + {line:highlight.lineno-1,ch:0}); } else if (window.eamy) { // not implemented } else if (window.textarea) { @@ -180,23 +180,25 @@ function doToggleBreakpoint(filename, url, sel) { .slideDown(); } else jQuery('.flash').hide(); - try { + //try { if (json.error) { window.location.href=json.redirect; } else { if (json.ok==true && window.mirror) { // mark the breakpoint if ok=True - editor.setMarker(json.lineno-1, - " %N%") + editor.setGutterMarker(json.lineno-1, "breakpoints", makeMarker()); + //editor.setMarker(json.lineno-1, + // " %N%") } else if (json.ok==false && window.mirror) { // remove mark if ok=False - editor.setMarker(json.lineno-1, "%N%") + editor.setGutterMarker(json.lineno-1, "breakpoints", null); + //editor.setMarker(json.lineno-1, "%N%") } else { // do nothing if ok = null } // alert(json.ok + json.lineno); } - } catch(e) { on_error(); } + //} catch(e) { on_error(); } }, error: function(json) { on_error(); } }); @@ -221,7 +223,7 @@ function doListBreakpoints(filename, url) { xhr.setRequestHeader('web2py-component-element', 'doListBreakpoints');}, success: function(json,text,xhr){ - try { + //try { if (json.error) { window.location.href=json.redirect; } else { @@ -229,12 +231,13 @@ function doListBreakpoints(filename, url) { for (i in json.breakpoints) { lineno = json.breakpoints[i]; // mark the breakpoint if ok=True - editor.setMarker(lineno-1, - " %N%"); + editor.setGutterMarker(lineno-1, "breakpoints", makeMarker()); + //editor.setMarker(lineno-1, + // " %N%"); } } } - } catch(e) { on_error(); } + //} catch(e) { on_error(); } }, error: function(json) { on_error(); } }); diff --git a/applications/admin/views/default/edit.html b/applications/admin/views/default/edit.html index cb1b6035..c72e69f8 100644 --- a/applications/admin/views/default/edit.html +++ b/applications/admin/views/default/edit.html @@ -12,6 +12,13 @@ + + + +{{if filetype=='python':}} + + +{{pass}} {{if TEXT_EDITOR_KEYBINDING == 'emacs':}}{{pass}} {{if TEXT_EDITOR_KEYBINDING == 'vi':}}{{pass}} @@ -154,74 +161,93 @@ jQuery(document).ready(function(){ {{elif TEXT_EDITOR == 'codemirror':}} {{elif TEXT_EDITOR == 'ace':}}
{{=data}}
@@ -291,19 +317,21 @@ window.onload = function() {

{{=T("Key bindings")}}

    {{=shortcut('Ctrl+S', T('Save via Ajax'))}} - {{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}} - {{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}} - {{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}} - {{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}} - {{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}} - {{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}} -
- {{else:}} -

{{=T("Key bindings")}}

-
    - {{=shortcut('Ctrl+S', T('Save via Ajax'))}} -
- {{pass}} - + {{if filetype=='python':}} + {{=shortcut('Ctrl+space', T('Autocomplete'))}} + {{pass}} + {{=shortcut('Ctrl+F11', T('Toggle Fullscreen'))}} + {{=shortcut('Ctrl-F / Cmd-F', T('Start searching'))}} + {{=shortcut('Ctrl-G / Cmd-G', T('Find Next'))}} + {{=shortcut('Shift-Ctrl-G / Shift-Cmd-G', T('Find Previous'))}} + {{=shortcut('Shift-Ctrl-F / Cmd-Option-F', T('Replace'))}} + {{=shortcut('Shift-Ctrl-R / Shift-Cmd-Option-F', T('Replace All'))}} + + {{else:}} +

{{=T("Key bindings")}}

+
    + {{=shortcut('Ctrl+S', T('Save via Ajax'))}} +
+ {{pass}} From b1f74213c61536b5a1851a5ddc2b39fc84398801 Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 17 Jan 2013 11:08:32 -0600 Subject: [PATCH 68/75] fixed issue 1279, sys.prefix in tickets, thanks mjmare --- VERSION | 2 +- gluon/restricted.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 62353f59..90491d6f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.17.11.00.19 +Version 2.4.1-alpha.2+timestamp.2013.01.17.11.08.02 diff --git a/gluon/restricted.py b/gluon/restricted.py index 0416dea2..c6649601 100644 --- a/gluon/restricted.py +++ b/gluon/restricted.py @@ -243,7 +243,7 @@ def snapshot(info=None, context=5, code=None, environment=None): # create a snapshot dict with some basic information s = {} - s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + s['pyver'] = 'Python ' + sys.version.split()[0] + ': ' + sys.executable + ' (prefix: %s)' % sys.prefix s['date'] = time.ctime(time.time()) # start to process frames From a2aa738f19662ab96444bb137e6df06ca0c98dba Mon Sep 17 00:00:00 2001 From: Massimo Date: Thu, 17 Jan 2013 11:34:52 -0600 Subject: [PATCH 69/75] removed un-necessary code, thanks Mariano --- VERSION | 2 +- applications/admin/views/debug/interact.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 90491d6f..24d61477 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.17.11.08.02 +Version 2.4.1-alpha.2+timestamp.2013.01.17.11.34.20 diff --git a/applications/admin/views/debug/interact.html b/applications/admin/views/debug/interact.html index cd2bf790..3b982e26 100644 --- a/applications/admin/views/debug/interact.html +++ b/applications/admin/views/debug/interact.html @@ -116,7 +116,7 @@ - {{pass}} - {{pass}} -
-

In file: {{=layer}}

- {{=CODE(code.replace('\r',''),language='python',link='/examples/global/vars/')}} +

{{=T('Traceback')}}

+
{{=traceback}}
+{{if snapshot:}} +{{try:}} + +

{{=T('Error snapshot')}} + + {{=helpicon()}} + {{=T("Detailed traceback description")}} + +

+ +
+ +

+ {{=snapshot['etype']}}({{=snapshot['evalue']}}) +

+

+ {{=T('inspect attributes')}} +

+
+
+

{{=T("Exception instance attributes")}}

+ + + {{for k,v in snapshot['exception'].items():}} + + + + + {{pass}} + +
{{=k}}{{=v}}
+
+
+
+ +
+

{{=T('Frames')}}

+
    + {{for i, frame in enumerate(snapshot['frames']):}} +
  • + {{is_hidden = (i != len(snapshot['frames'])-1 and 'hide' or 'inspect')}} +
    +

    + File {{="%s in %s at line %s" % (frame['file'], frame['func'], frame['lnum'])}} + {{=T("code")}} + {{=T("arguments")}} + {{=T("variables")}} +

    +
    +
    Function argument list
    +

    {{=frame['call']}}

    -{{else:}} -

    {{=T('Ticket Missing')}}

    +
    +
    Code listing
    + {{if frame['lines']:}} +
    {{=CODE('\n'.join([x[1] for x in sorted(frame['lines'].items(),key=lambda x: x[0])]),
    +	    language='python', link=None, counter=min(frame['lines'].keys()), highlight_line=frame['lnum'])}}
    + {{pass}} +
    +
    +
    Variables
    + + + {{for k,v in frame['dump'].items():}} + + + + + {{pass}} + +
    {{=k}}{{=v}}
    +
    +
    +
  • + {{pass}} +
+
+ +
+

Context

+

+ {{=T('locals')}} + {{=T('request')}} + {{=T('session')}} + {{=T('response')}} +

+

locals

+ {{=BEAUTIFY(snapshot.get('locals','no locals available in snapshot'))}} +
+

request

+ {{=XML(snapshot.get('request','no request available in snapshot'))}} +
+

session

+ {{=XML(snapshot.get('session','no session available in snapshot'))}} +
+

response

+ {{=XML(snapshot.get('response','no response available in snapshot'))}} +
+
+{{except Exception, e:}} + +{{import traceback;tb=traceback.format_exc().replace("\n","\\n") }} + {{pass}} - \ No newline at end of file +{{pass}} + +
+

In file: {{=layer}}

+ {{=CODE(code.replace('\r',''),language='python',link='/examples/global/vars/')}} +
+{{else:}} +

{{=T('Ticket Missing')}}

+{{pass}} + diff --git a/gluon/html.py b/gluon/html.py index 265f5500..c8e37ed8 100644 --- a/gluon/html.py +++ b/gluon/html.py @@ -2243,9 +2243,9 @@ class BEAUTIFY(DIV): for c in self.components: if hasattr(c, 'value') and not callable(c.value): if c.value: - components.append(c.value) + components.append(c.value) if hasattr(c, 'xml') and callable(c.xml): - components.append(c) + c = str(c) continue elif hasattr(c, 'keys') and callable(c.keys): rows = [] diff --git a/gluon/restricted.py b/gluon/restricted.py index c6649601..7d5ca513 100644 --- a/gluon/restricted.py +++ b/gluon/restricted.py @@ -16,7 +16,7 @@ import logging from storage import Storage from http import HTTP -from html import BEAUTIFY +from html import BEAUTIFY, XML logger = logging.getLogger("web2py") @@ -131,7 +131,7 @@ class RestrictedError(Exception): try: self.traceback = traceback.format_exc() except: - self.traceback = 'no traceback because template parting error' + self.traceback = 'no traceback because template parsing error' try: self.snapshot = snapshot(context=10, code=code, environment=self.environment) @@ -319,6 +319,6 @@ def snapshot(info=None, context=5, code=None, environment=None): # add web2py environment variables for k, v in environment.items(): if k in ('request', 'response', 'session'): - s[k] = BEAUTIFY(v) + s[k] = XML(str(BEAUTIFY(v))) return s diff --git a/gluon/tools.py b/gluon/tools.py index fec9b7cd..388c1178 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -5091,7 +5091,7 @@ class Wiki(object): '%(slug)s')), comment=current.T( "Choose Template or empty for new Page"))) - form = SQLFORM.factory(*fields, _class="well span6") + form = SQLFORM.factory(*fields, **dict(_class="well span6")) form.element("[type=submit]").attributes["_value"] = \ current.T("Create Page from Slug") From ea01ec0f0e5f70aebbedfc8d35378221321d40b6 Mon Sep 17 00:00:00 2001 From: Massimo Date: Fri, 18 Jan 2013 09:05:39 -0600 Subject: [PATCH 71/75] fixed issue 1289, fixed wiki templates, thanks Alan --- VERSION | 2 +- gluon/tools.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/VERSION b/VERSION index 8fb8ecd6..41ce4266 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.17.17.32.33 +Version 2.4.1-alpha.2+timestamp.2013.01.18.09.04.49 diff --git a/gluon/tools.py b/gluon/tools.py index 388c1178..b114c189 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -4762,10 +4762,8 @@ class Wiki(object): perms = self.manage_permissions = manage_permissions self.restrict_search = restrict_search self.extra = extra or {} - if templates is None and not manage_permissions: - templates = db.auth_wiki.tags.contains('template')&\ - db.auth_wiki.can_read.contains('everybody') self.templates = templates + table_definitions = [ ('wiki_page', { 'args':[ @@ -4817,6 +4815,10 @@ class Wiki(object): args += value['args'] db.define_table(key, *args, **value['vars']) + if self.templates is None and not self.manage_permissions: + self.templates = db.wiki_page.tags.contains('template')&\ + db.wiki_page.can_read.contains('everybody') + def update_tags_insert(page, id, db=db): for tag in page.tags or []: tag = tag.strip().lower() From e90000545b505c62ceabce3db69b8a150f94b0e9 Mon Sep 17 00:00:00 2001 From: Massimo Date: Fri, 18 Jan 2013 09:09:21 -0600 Subject: [PATCH 72/75] highligthing wiki menu items, 1290, thanks Paolo --- VERSION | 2 +- gluon/tools.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 41ce4266..3ba35bcf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.18.09.04.49 +Version 2.4.1-alpha.2+timestamp.2013.01.18.09.08.52 diff --git a/gluon/tools.py b/gluon/tools.py index b114c189..935828c0 100644 --- a/gluon/tools.py +++ b/gluon/tools.py @@ -5153,15 +5153,19 @@ class Wiki(object): base = match.group('base').replace(' ', '') title = match.group('title') link = match.group('link') + title_page = None if link.startswith('@'): items = link[2:].split('/') if len(items) > 3: + title_page = items[3] link = URL(a=items[0] or None, c=items[1] or None, f=items[2] or None, args=items[3:]) parent = tree.get(base[1:], tree['']) subtree = [] tree[base] = subtree - parent.append((current.T(title), False, link, subtree)) + parent.append((current.T(title), + request.args(0) == title_page, + link, subtree)) if self.can_see_menu(): submenu = [] menu.append((current.T('[Wiki]'), None, None, submenu)) From 7b4a0515becf3a6b7ffd145d7a1e00c11ede9b91 Mon Sep 17 00:00:00 2001 From: Massimo Date: Fri, 18 Jan 2013 09:14:19 -0600 Subject: [PATCH 73/75] fixed issue 1291, casting to string for like operator, thanks Dominic --- VERSION | 2 +- gluon/dal.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 3ba35bcf..45d8fe86 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.18.09.08.52 +Version 2.4.1-alpha.2+timestamp.2013.01.18.09.13.47 diff --git a/gluon/dal.py b/gluon/dal.py index b686d6f5..72a135fe 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -2570,12 +2570,18 @@ class PostgreSQLAdapter(BaseAdapter): def LIKE(self,first,second): - return '(%s LIKE %s)' % (self.expand(first), - self.expand(second,'string')) + args = (self.expand(first), self.expand(second,'string')) + if not first.type in ('string', 'text', 'json'): + return '(CAST(%s AS CHAR) LIKE %s)' % args + else: + return '(%s LIKE %s)' % args def ILIKE(self,first,second): - return '(%s ILIKE %s)' % (self.expand(first), - self.expand(second,'string')) + args = (self.expand(first), self.expand(second,'string')) + if not first.type in ('string', 'text', 'json'): + return '(CAST(%s AS CHAR) ILIKE %s)' % args + else: + return '(%s ILIKE %s)' % args def REGEXP(self,first,second): return '(%s ~ %s)' % (self.expand(first), From 51f5e40b3785f62793c98c7d38211c8c7675e40d Mon Sep 17 00:00:00 2001 From: Massimo Date: Fri, 18 Jan 2013 09:20:30 -0600 Subject: [PATCH 74/75] fatch IMAP inbox size in dal, thanks Alan --- VERSION | 2 +- gluon/dal.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 45d8fe86..329ee513 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.18.09.13.47 +Version 2.4.1-alpha.2+timestamp.2013.01.18.09.19.59 diff --git a/gluon/dal.py b/gluon/dal.py index 72a135fe..b9e47944 100644 --- a/gluon/dal.py +++ b/gluon/dal.py @@ -6033,7 +6033,7 @@ class IMAPAdapter(NoSQLAdapter): messages_set = messages_set[int(limitby[0]):int(limitby[1])] # keep the requests small for header/flags - if any([(field.name in ["content", + if any([(field.name in ["content", "size", "attachments", "email"]) for field in fields]): imap_fields = "(RFC822 FLAGS)" From 317d910cd3719ed12d2780dc7f1a30abadb634ec Mon Sep 17 00:00:00 2001 From: Massimo Date: Fri, 18 Jan 2013 10:01:01 -0600 Subject: [PATCH 75/75] .vtt in contenttype, thanks Magnitus --- VERSION | 2 +- gluon/contenttype.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 329ee513..47f5c491 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 2.4.1-alpha.2+timestamp.2013.01.18.09.19.59 +Version 2.4.1-alpha.2+timestamp.2013.01.18.10.00.28 diff --git a/gluon/contenttype.py b/gluon/contenttype.py index 0a3cb568..d01a8af1 100644 --- a/gluon/contenttype.py +++ b/gluon/contenttype.py @@ -625,6 +625,7 @@ CONTENT_TYPE = { '.voc': 'audio/x-voc', '.vor': 'application/vnd.stardivision.writer', '.vst': 'image/x-tga', + '.vtt': 'text/vtt', '.wav': 'audio/x-wav', '.wax': 'audio/x-ms-asx', '.wb1': 'application/x-quattropro',