ImageMetaTag.ImageDict¶
-This submodule contains the ImageMetaTag.ImageDict
class, and functions for preparing them.
The purpose of an ImageMetaTag.ImageDict
is to sort the image metadata, supplied to
-ImageMetaTag.savefig()
and usually stored in a database file, into a useful form that
-can quickly and easily be presented as a webpage by ImageMetaTag.webpage.write_full_page()
.
This submodule contains the ImageMetaTag.ImageDict class, and functions for +preparing instances of it.
+The purpose of an ImageMetaTag.ImageDict is to sort the image metadata, supplied to +ImageMetaTag.savefig() and usually stored in a database file, into a useful form that +can quickly and easily be presented as a webpage by ImageMetaTag.webpage.write_full_page().
An easy example of creating a webpage, using an ImageDict is shown in simplest_image_dict.py
The ImageDict Class¶
-
-class
ImageMetaTag.
ImageDict
(input_dict, level_names=None, selector_widths=None, selector_animated=None, animation_direction=None)[source]¶
+class ImageMetaTag.ImageDict(input_dict, level_names=None, selector_widths=None, selector_animated=None, animation_direction=None)¶
A class which holds a heirachical dictionary of dictionaries, and the associated methods for appending/removing dictionaries from it.
The expected use case for the dictionary is to represent a large set of images, @@ -114,7 +114,7 @@
The ImageDict Class
ImageMetaTag.dict_heirachy_from_list()
+of metadata items, use ImageMetaTag.dict_heirachy_from_list()- Options:
-
@@ -137,33 +137,33 @@
-
-
append
(new_dict, devmode=False, skip_key_relist=False)[source]¶
+append(new_dict, devmode=False, skip_key_relist=False)¶
appends a new dictionary (with a single element in each layer!) into a current ImageDict.
The skip_key_relist option can be set to True to stop the regeneration of key lists.
The ImageDict Class
-
-
-
-
copy_except_dict_and_keys
()[source]¶
+copy_except_dict_and_keys()¶
returns a copy of an ImageDict except it will have null values for the dict and keys
-
-
dict_depth
(uniform_depth=False)[source]¶
+dict_depth(uniform_depth=False)¶
Uses dict_depths to get the depth of all branches of the plot_dict and, if required, checks they all equal the max
-
-
dict_depths
(in_dict, depth=0)[source]¶
+dict_depths(in_dict, depth=0)¶
Recursively finds the depth of a ImageDict and returns a list of lists
-
-
dict_index_array
(devmode=False, maxdepth=None, verbose=False)[source]¶
+dict_index_array(devmode=False, maxdepth=None, verbose=False)¶
Using the list of dictionary keys (at each level of a uniform_depth dictionary of dictionaries), this produces a list of the indices that can be used to reference the keys to get the result for each element.
@@ -178,13 +178,13 @@The ImageDict Class
- -
+dict_print(in_dict, indent=0, outstr='')¶dict_print
(in_dict, indent=0, outstr='')[source]¶recursively details a dictionary of dictionaries, with indentation, to a string
- -
-
-
dict_prune
(in_dict, dicts_pruned=False)[source]¶
+dict_prune(in_dict, dicts_pruned=False)¶
Prunes the ImageDict of empty, unterminated, branches (which occur after parts have been removed). Returns True if a dict was pruned, False if not.
@@ -192,7 +192,7 @@The ImageDict Class
- -
+dict_remove(in_dict, rm_dict)¶dict_remove
(in_dict, rm_dict)[source]¶removes a dictionary of dictionaries from another, larger, one. This can leave empty branches, at multiple levels of the dict, so needs cleaning up afterwards.
@@ -200,25 +200,25 @@The ImageDict Class
- -
+dict_union(in_dict, new_dict)¶dict_union
(in_dict, new_dict)[source]¶produces the union of a dictionary of dictionaries
- -
-
-
key_at_depth
(in_dict, depth)[source]¶
+key_at_depth(in_dict, depth)¶
returns the keys of a dictionary, at a given depth
-
-
keys_by_depth
(in_dict, depth=0, keys=None, subdirs=None)[source]¶
+keys_by_depth(in_dict, depth=0, keys=None, subdirs=None)¶
Returns:
- a dictionary of sets, containing the keys at each level of the dictionary (keyed by the level number). @@ -228,7 +228,7 @@
-
-
list_keys_by_depth
(devmode=False)[source]¶
+list_keys_by_depth(devmode=False)¶
Lists the keys of the dictionary to create a list of keys, for each level of the dictionary, up to its depth.
It is usually much faster to create an ImageDict by appending images to it, @@ -242,7 +242,7 @@
The ImageDict Class
- -
+mergedicts(dict1, dict2)¶mergedicts
(dict1, dict2)[source]¶Alternative version of dict_union using generators which is much faster for large dicts but needs to be converted to a dict when it’s called: new_dict = dict(mergdicts(dict1,dict))
@@ -250,7 +250,7 @@The ImageDict Class
- -
+remove(rm_dict, skip_key_relist=False)¶remove
(rm_dict, skip_key_relist=False)[source]¶removes a dictionary from within an ImageDict. The skip_key_relist option can be set to True to stop the regeneration of key lists.
TIP: Because the remove process needs to prune empty sections afterwards, @@ -261,7 +261,7 @@
The ImageDict Class
- -
+return_from_list(vals_at_depth)¶return_from_list
(vals_at_depth)[source]¶Returns the end values of ImageDict, when given a list of values for the keys at different depths. Returns None if the set of values is not contained in the ImageDict.
@@ -272,7 +272,7 @@The ImageDict Class
- -
+return_key_inds(in_dict, out_array=None, this_set_of_inds=None, depth=None, level=None, verbose=False, devmode=False)¶return_key_inds
(in_dict, out_array=None, this_set_of_inds=None, depth=None, level=None, verbose=False, devmode=False)[source]¶Does the work for dict_index_array, by recursively adding indices to the keys to a current list, and branching where required, and adding compelted lists to the out_array
@@ -280,7 +280,7 @@The ImageDict Class
- -
+sort_keys(sort_methods, devmode=False)¶sort_keys
(sort_methods, devmode=False)[source]¶Sorts the keys of a plot dictionary, according to a particular sort method (or a list of sort methods that matches the number of keys).
-
@@ -304,18 +304,18 @@
-
-
ImageMetaTag.
readmeta_from_image
(img_file, img_format=None)[source]¶
+ImageMetaTag.readmeta_from_image(img_file, img_format=None)¶
Reads the metadata added by the ImageMetaTag savefig, from an image file, and returns a dictionary of tagname: value pairs
-
-
ImageMetaTag.
dict_heirachy_from_list
(in_dict, payload, heirachy)[source]¶
+ImageMetaTag.dict_heirachy_from_list(in_dict, payload, heirachy)¶
Converts a flat dictionary of tagname: value pairs, into an ordered dictionary of dictionaries according to the input heirachy (which is a list of tagnames).
The output dictionary will only have one element per level, but can be used to create or -append into an
@@ -324,7 +324,7 @@ImageMetaTag.ImageDict
. +append into an ImageMetaTag.ImageDict. The final level will be the ‘payload’ input, which is the object the dictionary, with all it’s levels, is describing. The payload would usually be the full/relative path of the image file, or list of image files.Functions useful in preparing ImageDicts
- -
+ImageMetaTag.dict_split(in_dict, n_split=None, size_split=None, extra_opts=None)¶ImageMetaTag.
dict_split
(in_dict, n_split=None, size_split=None, extra_opts=None)[source]¶Generator that breaks up a flat dictionary and yields a set of sub-dictionaries in n_split chunks, or size_split in size. It is split on it’s first level, not recursively.
It is very useful for splitting large dictionaries of image metadata @@ -345,7 +345,7 @@
Functions useful in preparing ImageDicts
- -
+ImageMetaTag.simple_dict_filter(simple_dict, tests, raise_key_mismatch=False)¶ImageMetaTag.
simple_dict_filter
(simple_dict, tests, raise_key_mismatch=False)[source]¶Tests the contents of a simple, un-heirachical dict (properties an image) against a set of tests.
An example set of tests: @@ -368,7 +368,7 @@
Functions useful in preparing ImageDicts
- -
+ImageMetaTag.check_for_required_keys(img_info, req_keys)¶ImageMetaTag.
check_for_required_keys
(img_info, req_keys)[source]¶Checks an img_info dictionary has a set of required keys, specifed as a list of strings
Returns True or False accordingly.
- -
The ImageDict Class¶
Functions useful in preparing ImageDicts
- -
The ImageDict Class
Navigation
-
@@ -397,12 +397,12 @@
Navigation
- previous | -
- ImageMetaTag 0.6.4 documentation » +
- ImageMetaTag 0.6.5 documentation »
Navigation
+-
+
- + index +
- + modules | +
- ImageMetaTag 0.6.5 documentation » +
- Module code » +
Quick search
+ ++ Enter search terms or a module, class or function name. +
+Source code for ImageMetaTag
+'''
+ImageMetaTag is a python package built around a wrapper for
+`savefig <http://matplotlib.org/api/pyplot_api.html?highlight=savefig#matplotlib.pyplot.savefig>`_
+in
+`matplotlib <http://matplotlib.org/>`_, which adds metadata tags to supported image file formats.
+
+Once the images have been tagged, it can also be used to manage an
+`SQL database <https://docs.python.org/2/library/sqlite3.html>`_ of images and their metadata.
+The image metadata can be used to produce an
+:class:`ImageMetaTag.ImageDict` object: a structured/heirachical dictionary of dictionaries
+which can be used to easily create web pages to present large numbers of images.
+
+As the image metadata tagging process involves reading the image using the Image library,
+a few common image post-processing options are included such as cropping, logo addition and
+colour palette manipulation to reduce filesizes.
+
+.. moduleauthor:: Malcolm Brooks https://github.com/malcolmbrooks
+
+(C) Crown copyright Met Office. All rights reserved.
+Released under BSD 3-Clause License. See LICENSE for more details.
+'''
+
+__version__ = '0.6.5'
+# for consistency, the version here should match:
+# ImageMetaTag/__init__.py
+# ImageMetaTag/javascript/imt_dropdown.js
+# docs/source/conf.py
+# setup.py
+__documentation__ = 'http://scitools-incubator.github.io/image-meta-tag/build/html/'
+
+# list fo file formats which are valid for saving metadata to:
+META_IMG_FORMATS = ['png']
+# and which can do image post-processing
+POSTPROC_IMG_FORMATS = ['png']
+
+# default timeout and retries for database access:
+DEFAULT_DB_TIMEOUT = 6
+DEFAULT_DB_ATTEMPTS = 20
+
+from ImageMetaTag.savefig import savefig, image_file_postproc
+from ImageMetaTag.img_dict import ImageDict, readmeta_from_image, dict_heirachy_from_list, \
+ dict_split, simple_dict_filter, check_for_required_keys
+# we want all of the functions in webpage and db, as a separate level
+import ImageMetaTag.webpage, ImageMetaTag.db
+
Navigation
+-
+
- + index +
- + modules | +
- ImageMetaTag 0.6.5 documentation » +
- Module code » +
Navigation
-
@@ -38,23 +36,27 @@
Navigation
- modules | -
- ImageMetaTag 0.6.4 documentation » -
- Module code » +
- ImageMetaTag 0.6.5 documentation » +
- Module code » +
- ImageMetaTag »
Quick search
++ Enter search terms or a module, class or function name. +
Quick search
Source code for ImageMetaTag.db
-'''
+'''
This module contains a set of functions to create/write to/read
and maintain an sqlite3 database of image files and their associated metadata.
@@ -77,26 +79,26 @@ Source code for ImageMetaTag.db
import os, sqlite3, fnmatch, time, errno, pdb
-from ImageMetaTag import META_IMG_FORMATS, DEFAULT_DB_TIMEOUT, DEFAULT_DB_ATTEMPTS
-from ImageMetaTag.img_dict import readmeta_from_image, check_for_required_keys
+from ImageMetaTag import META_IMG_FORMATS, DEFAULT_DB_TIMEOUT, DEFAULT_DB_ATTEMPTS
+from ImageMetaTag.img_dict import readmeta_from_image, check_for_required_keys
-from datetime import datetime
-import numpy as np
-from cStringIO import StringIO
+from datetime import datetime
+import numpy as np
+from cStringIO import StringIO
-# the name of the database table that holds the sqlite database of plot metadata
-SQLITE_IMG_INFO_TABLE = 'img_info'
+# the name of the database table that holds the sqlite database of plot metadata
+SQLITE_IMG_INFO_TABLE = 'img_info'
[docs]def info_key_to_db_name(in_str):
- 'Consistently convert a name in the img_info dict to something to be used in the database'
- return in_str.replace(' ', '__')
-
+ 'Consistently convert a name in the img_info dict to something to be used in the database'
+ return in_str.replace(' ', '__')
+
[docs]def db_name_to_info_key(in_str):
- 'Inverse of info_key_to_db_name'
- # convert to string, to remove unicode string
- return str(in_str).replace('__', ' ')
-
-[docs]def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False,
+ 'Inverse of info_key_to_db_name'
+ # convert to string, to remove unicode string
+ return str(in_str).replace('__', ' ')
+
+[docs]def write_img_to_dbfile(db_file, img_filename, img_info, add_strict=False,
timeout=DEFAULT_DB_TIMEOUT):
'''
Writes image metadata to a database.
@@ -119,19 +121,19 @@ Source code for ImageMetaTag.db
'''
if len(img_info) == 0:
- raise ValueError('Size of image info dict is zero')
- if db_file is None:
+ raise ValueError('Size of image info dict is zero')
+ if db_file is None:
pass
else:
- # open the database:
+ # open the database:
dbcn, dbcr = open_or_create_db_file(db_file, img_info, timeout=timeout)
- # now write:
+ # now write:
write_img_to_open_db(dbcr, img_filename, img_info, add_strict=add_strict)
- # now commit that databasde entry and close:
+ # now commit that databasde entry and close:
dbcn.commit()
- dbcn.close()
-
-
+[docs]def read(db_file, required_tags=None, tag_strings=None,
db_timeout=DEFAULT_DB_TIMEOUT,
db_attempts=DEFAULT_DB_ATTEMPTS):
'''
@@ -160,52 +162,52 @@ Source code for ImageMetaTag.db
In older versions, this was named read_img_info_from_dbfile which will still work.
'''
- if db_file is None:
- return None, None
+ if db_file is None:
+ return None, None
else:
if not os.path.isfile(db_file):
- return None, None
+ return None, None
else:
n_tries = 1
- read_db = False
+ read_db = False
while not read_db and n_tries <= db_attempts:
try:
- # open the connection and the cursor:
+ # open the connection and the cursor:
dbcn, dbcr = open_db_file(db_file, timeout=db_timeout)
- # read it:
+ # read it:
f_list, out_dict = read_img_info_from_dbcursor(dbcr,
required_tags=required_tags,
tag_strings=tag_strings)
- # close connection:
+ # close connection:
dbcn.close()
- read_db = True
+ read_db = True
except sqlite3.OperationalError as OpErr:
- if 'database is locked' in OpErr.message:
- # database being locked is what the retries and timeouts are for:
- print '%s database timeout reading from file "%s", %s s' \
+ if 'database is locked' in OpErr.message:
+ # database being locked is what the retries and timeouts are for:
+ print '%s database timeout reading from file "%s", %s s' \
% (dt_now_str(), db_file, n_tries * db_timeout)
n_tries += 1
- elif OpErr.message == 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE):
- # the db file exists, but it doesn't have anything in it:
- return None, None
+ elif OpErr.message == 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE):
+ # the db file exists, but it doesn't have anything in it:
+ return None, None
else:
- # everything else needs to be reported and raised immediately:
- msg = '{} for file {}'.format(OpErr.message, db_file)
+ # everything else needs to be reported and raised immediately:
+ msg = '{} for file {}'.format(OpErr.message, db_file)
raise sqlite3.OperationalError(msg)
- # if we went through all the attempts then it is time to raise the error:
+ # if we went through all the attempts then it is time to raise the error:
if n_tries > db_attempts:
- msg = '{} for file {}'.format(OpErr.message, db_file)
+ msg = '{} for file {}'.format(OpErr.message, db_file)
raise sqlite3.OperationalError(msg)
- # close connection:
+ # close connection:
dbcn.close()
- return f_list, out_dict
-
+ return f_list, out_dict
+
read_img_info_from_dbfile = read
-[docs]def merge_db_files(main_db_file, add_db_file, delete_add_db=False,
- delete_added_entries=False,
+[docs]def merge_db_files(main_db_file, add_db_file, delete_add_db=False,
+ delete_added_entries=False,
db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS):
'''
Merges two ImageMetaTag database files, with the contents of add_db_file added
@@ -222,48 +224,48 @@ Source code for ImageMetaTag.db
databases. It does nothing if delete_add_db is True.
'''
- # read what we want to add in:
+ # read what we want to add in:
add_filelist, add_tags = read(add_db_file, db_timeout=db_timeout, db_attempts=db_attempts)
- if add_filelist is not None:
+ if add_filelist is not None:
if len(add_filelist) > 0:
n_tries = 1
- wrote_db = False
+ wrote_db = False
while not wrote_db and n_tries <= db_attempts:
try:
- # open the main database
+ # open the main database
dbcn, dbcr = open_db_file(main_db_file, timeout=db_timeout)
- # and add in the new contents:
+ # and add in the new contents:
for add_file, add_info in add_tags.iteritems():
write_img_to_open_db(dbcr, add_file, add_info)
dbcn.commit()
- # if we got here, then we're good!
- wrote_db = True
- # finally close:
+ # if we got here, then we're good!
+ wrote_db = True
+ # finally close:
dbcn.close()
except sqlite3.OperationalError as OpErr:
- if 'database is locked' in OpErr.message:
- # database being locked is what the retries and timeouts are for:
- print '%s database timeout writing to file "%s", %s s' \
+ if 'database is locked' in OpErr.message:
+ # database being locked is what the retries and timeouts are for:
+ print '%s database timeout writing to file "%s", %s s' \
% (dt_now_str(), main_db_file, n_tries * db_timeout)
n_tries += 1
else:
- # everything else needs to be reported and raised immediately:
- msg = '{} for file {}'.format(OpErr.message, main_db_file)
+ # everything else needs to be reported and raised immediately:
+ msg = '{} for file {}'.format(OpErr.message, main_db_file)
raise sqlite3.OperationalError(msg)
- # if we went through all the attempts then it is time to raise the error:
+ # if we went through all the attempts then it is time to raise the error:
if n_tries > db_attempts:
- msg = '{} for file {}'.format(OpErr.message, main_db_file)
+ msg = '{} for file {}'.format(OpErr.message, main_db_file)
raise sqlite3.OperationalError(msg)
- # delete or tidy:
+ # delete or tidy:
if delete_add_db:
rmfile(add_db_file)
elif delete_added_entries:
- del_plots_from_dbfile(add_db_file, add_filelist, do_vacuum=False,
- allow_retries=True, skip_warning=True)
-
-[docs]def open_or_create_db_file(db_file, img_info, restart_db=False, timeout=DEFAULT_DB_TIMEOUT):
+ del_plots_from_dbfile(add_db_file, add_filelist, do_vacuum=False,
+ allow_retries=True, skip_warning=True)
+
+[docs]def open_or_create_db_file(db_file, img_info, restart_db=False, timeout=DEFAULT_DB_TIMEOUT):
'''
Opens a database file and sets up initial tables, then returns the connection and cursor.
@@ -281,27 +283,27 @@ Source code for ImageMetaTag.db
if not os.path.isfile(db_file) or restart_db:
if os.path.isfile(db_file):
os.remove(db_file)
- # create a new database file:
+ # create a new database file:
dbcn = sqlite3.connect(db_file)
dbcr = dbcn.cursor()
- # and create the table:
+ # and create the table:
create_table_for_img_info(dbcr, img_info)
else:
- # open the database file:
+ # open the database file:
dbcn, dbcr = open_db_file(db_file, timeout=timeout)
- # check for the required table:
+ # check for the required table:
table_names = list_tables(dbcr)
if SQLITE_IMG_INFO_TABLE not in table_names:
- # create it if required:
+ # create it if required:
create_table_for_img_info(dbcr, img_info)
- return dbcn, dbcr
-
+ return dbcn, dbcr
+
def create_table_for_img_info(dbcr, img_info):
- 'Creates a database table, in a database cursor, to store for the input img_info'
- create_command = 'CREATE TABLE {}(fname TEXT PRIMARY KEY,'.format(SQLITE_IMG_INFO_TABLE)
+ 'Creates a database table, in a database cursor, to store for the input img_info'
+ create_command = 'CREATE TABLE {}(fname TEXT PRIMARY KEY,'.format(SQLITE_IMG_INFO_TABLE)
for key in img_info.keys():
- create_command += ' "{}" TEXT,'.format(info_key_to_db_name(key))
- create_command = create_command[0:-1] + ')'
+ create_command += ' "{}" TEXT,'.format(info_key_to_db_name(key))
+ create_command = create_command[0:-1] + ')'
dbcr.execute(create_command)
[docs]def open_db_file(db_file, timeout=DEFAULT_DB_TIMEOUT):
@@ -314,8 +316,8 @@ Source code for ImageMetaTag.db
dbcn = sqlite3.connect(db_file, timeout=timeout)
dbcr = dbcn.cursor()
- return dbcn, dbcr
-
+ return dbcn, dbcr
+
[docs]def read_db_file_to_mem(db_file, timeout=DEFAULT_DB_TIMEOUT):
'''
Opens a pre-existing database file into a copy held in memory. This can be accessed much
@@ -330,24 +332,24 @@ Source code for ImageMetaTag.db
Returns an open database connection (dbcn) and cursor (dbcr)
'''
- # read the database into an in-memory file object:
- #dbcn = sqlite3.connect(db_file)
+ # read the database into an in-memory file object:
+ #dbcn = sqlite3.connect(db_file)
dbcn, _ = open_db_file(db_file, timeout=timeout)
memfile = StringIO()
for line in dbcn.iterdump():
- memfile.write('%s\n' % line)
+ memfile.write('%s\n' % line)
dbcn.close()
memfile.seek(0)
- # Create a database in memory and import from memfile
- dbcn = sqlite3.connect(":memory:")
+ # Create a database in memory and import from memfile
+ dbcn = sqlite3.connect(":memory:")
dbcn.cursor().executescript(memfile.read())
dbcn.commit()
dbcr = dbcn.cursor()
- return dbcn, dbcr
-
-[docs]def write_img_to_open_db(dbcr, filename, img_info, add_strict=False, attempt_replace=False):
+ return dbcn, dbcr
+
+[docs]def write_img_to_open_db(dbcr, filename, img_info, add_strict=False, attempt_replace=False):
'''
Does the work for write_img_to_dbfile to add an image to the open database cursor (dbcr)
@@ -357,44 +359,44 @@ Source code for ImageMetaTag.db
entry if the image is already present. Otherwise it will ignore it.
'''
- # now add in the information:
- # get the name of the fields from the cursor descripton:
- _ = dbcr.execute('select * from %s' % SQLITE_IMG_INFO_TABLE).fetchone()
+ # now add in the information:
+ # get the name of the fields from the cursor descripton:
+ _ = dbcr.execute('select * from %s' % SQLITE_IMG_INFO_TABLE).fetchone()
field_names = [r[0] for r in dbcr.description]
- # convert these to keys:
+ # convert these to keys:
field_names = [db_name_to_info_key(x) for x in field_names]
- # now build the command
- add_command = 'INSERT INTO {}(fname,'.format(SQLITE_IMG_INFO_TABLE)
+ # now build the command
+ add_command = 'INSERT INTO {}(fname,'.format(SQLITE_IMG_INFO_TABLE)
add_list = [filename]
for key, item in img_info.iteritems():
if key in field_names:
- add_command += ' "{}",'.format(info_key_to_db_name(key))
+ add_command += ' "{}",'.format(info_key_to_db_name(key))
add_list.append(item)
elif add_strict:
- raise ValueError('Attempting to add a line to the database that include invalid fields')
- # add in the right number of ?
- add_command = add_command[0:-1] + ') VALUES(' + '?,'*(len(add_list)-1) + '?)'
+ raise ValueError('Attempting to add a line to the database that include invalid fields')
+ # add in the right number of ?
+ add_command = add_command[0:-1] + ') VALUES(' + '?,'*(len(add_list)-1) + '?)'
try:
dbcr.execute(add_command, add_list)
except sqlite3.IntegrityError:
if attempt_replace:
- # try an INSERT OR REPLACE
- add_command.replace('INSERT ', 'INSERT OR REPLACE ')
- # if this fails, want it to report it's error message as is, so no 'try':
+ # try an INSERT OR REPLACE
+ add_command.replace('INSERT ', 'INSERT OR REPLACE ')
+ # if this fails, want it to report it's error message as is, so no 'try':
dbcr.execute(add_command, add_list)
else:
- # this file is already in the database (as the primary key, so do nothing...)
+ # this file is already in the database (as the primary key, so do nothing...)
pass
finally:
- pass
-
+ pass
+
def list_tables(dbcr):
- 'lists the tables present, from a database cursor'
- result = dbcr.execute("SELECT name FROM sqlite_master WHERE type='table';").fetchall()
+ 'lists the tables present, from a database cursor'
+ result = dbcr.execute("SELECT name FROM sqlite_master WHERE type='table';").fetchall()
table_names = sorted(zip(*result)[0])
return table_names
-[docs]def read_img_info_from_dbcursor(dbcr, required_tags=None, tag_strings=None):
+[docs]def read_img_info_from_dbcursor(dbcr, required_tags=None, tag_strings=None):
'''
Reads from an open database cursor (dbcr) for :func:`ImageMetaTag.db.read` and other routines.
@@ -402,15 +404,15 @@ Source code for ImageMetaTag.db
* required_tags - a list of image tags to return, and to fail if not all are present
* tag_strings - an input list that will be populated with the unique values of the image tags
'''
- # read in the data from the database:
- db_contents = dbcr.execute('select * from %s' % SQLITE_IMG_INFO_TABLE).fetchall()
- # and convert that to a useful dict/list combo:
+ # read in the data from the database:
+ db_contents = dbcr.execute('select * from %s' % SQLITE_IMG_INFO_TABLE).fetchall()
+ # and convert that to a useful dict/list combo:
filename_list, out_dict = process_select_star_from(db_contents, dbcr,
required_tags=required_tags,
tag_strings=tag_strings)
- return filename_list, out_dict
-
-[docs]def process_select_star_from(db_contents, dbcr, required_tags=None, tag_strings=None):
+ return filename_list, out_dict
+
+[docs]def process_select_star_from(db_contents, dbcr, required_tags=None, tag_strings=None):
'''
Converts the output from a select * from .... command into a standard output format
Requires a database cursor (dbcr) to identify the field names.
@@ -425,28 +427,28 @@ Source code for ImageMetaTag.db
* a dictionary, by filename, containing a dictionary of the image metadata \
as tagname: value
'''
- # get the name of the fields from the cursor descripton:
+ # get the name of the fields from the cursor descripton:
out_dict = {}
filename_list = []
- # get the name of the fields from the cursor descripton:
+ # get the name of the fields from the cursor descripton:
field_names = [r[0] for r in dbcr.description]
- # the required_tags input is a list of tag names (as strings):
- if required_tags is not None:
+ # the required_tags input is a list of tag names (as strings):
+ if required_tags is not None:
if not isinstance(required_tags, list):
- raise ValueError('Input required_tags should be a list of strings')
+ raise ValueError('Input required_tags should be a list of strings')
else:
for test_str in required_tags:
if not isinstance(test_str, str):
- raise ValueError('Input required_tags should be a list of strings')
+ raise ValueError('Input required_tags should be a list of strings')
- if tag_strings is not None:
+ if tag_strings is not None:
if not isinstance(tag_strings, list):
- raise ValueError('Input tag_strings should be a list')
+ raise ValueError('Input tag_strings should be a list')
- # now iterate and make a dictionary to return,
- # with the tests outside the loops so they're not tested for every row and element:
- if required_tags is None and tag_strings is None:
+ # now iterate and make a dictionary to return,
+ # with the tests outside the loops so they're not tested for every row and element:
+ if required_tags is None and tag_strings is None:
for row in db_contents:
fname = str(row[0])
filename_list.append(fname)
@@ -454,10 +456,10 @@ Source code for ImageMetaTag.db
for tag_name, tag_val in zip(field_names[1:], row[1:]):
img_info[db_name_to_info_key(tag_name)] = str(tag_val)
out_dict[fname] = img_info
- # return None, None if the contents are empty:
+ # return None, None if the contents are empty:
if len(filename_list) == 0 and len(out_dict) == 0:
- return None, None
- elif required_tags is not None and tag_strings is None:
+ return None, None
+ elif required_tags is not None and tag_strings is None:
for row in db_contents:
fname = str(row[0])
filename_list.append(fname)
@@ -467,14 +469,14 @@ Source code for ImageMetaTag.db
if tag_name_full in required_tags:
img_info[tag_name_full] = str(tag_val)
if len(img_info) != len(required_tags):
- raise ValueError('Database entry does not contain all of the required_tags')
+ raise ValueError('Database entry does not contain all of the required_tags')
out_dict[fname] = img_info
- # return None, None if the contents are empty:
+ # return None, None if the contents are empty:
if len(filename_list) == 0 and len(out_dict) == 0:
- return None, None
- elif required_tags is None and tag_strings is not None:
- # we want all tags, but we want them as referneces to a common list:
+ return None, None
+ elif required_tags is None and tag_strings is not None:
+ # we want all tags, but we want them as referneces to a common list:
for row in db_contents:
fname = str(row[0])
filename_list.append(fname)
@@ -482,54 +484,54 @@ Source code for ImageMetaTag.db
for tag_name, tag_val in zip(field_names[1:], row[1:]):
str_tag_val = str(tag_val)
try:
- # loacate the tag_string in the list:
+ # loacate the tag_string in the list:
tag_index = tag_strings.index(str_tag_val)
- # and refernece it:
+ # and refernece it:
img_info[db_name_to_info_key(tag_name)] = tag_strings[tag_index]
except ValueError:
- # tag not yet in the tag_strings list, so
- # add the new string onto the end:
+ # tag not yet in the tag_strings list, so
+ # add the new string onto the end:
tag_strings.append(str_tag_val)
- # and reference it:
+ # and reference it:
img_info[db_name_to_info_key(tag_name)] = tag_strings[-1]
out_dict[fname] = img_info
- # return None, None if the contents are empty:
+ # return None, None if the contents are empty:
if len(filename_list) == 0 and len(out_dict) == 0:
- return None, None
+ return None, None
else:
- # we want to filter the tags, and we want them as referneces to a common list:
+ # we want to filter the tags, and we want them as referneces to a common list:
for row in db_contents:
fname = str(row[0])
filename_list.append(fname)
img_info = {}
for tag_name, tag_val in zip(field_names[1:], row[1:]):
- # test to see if the tag name is required:
+ # test to see if the tag name is required:
tag_name_full = db_name_to_info_key(tag_name)
if tag_name_full in required_tags:
str_tag_val = str(tag_val)
try:
- # loacate the tag_string in the list:
+ # loacate the tag_string in the list:
tag_index = tag_strings.index(str_tag_val)
- # and refernece it:
+ # and refernece it:
img_info[tag_name_full] = tag_strings[tag_index]
except ValueError:
- # tag not yet in the tag_strings list, so
- # add the new string onto the end:
+ # tag not yet in the tag_strings list, so
+ # add the new string onto the end:
tag_strings.append(str_tag_val)
- # and reference it:
+ # and reference it:
img_info[tag_name_full] = tag_strings[-1]
out_dict[fname] = img_info
- # return None, None if the contents are empty:
+ # return None, None if the contents are empty:
if len(filename_list) == 0 and len(out_dict) == 0:
- return None, None
-
+ return None, None
- # we're good, return the data:
- return filename_list, out_dict
-[docs]def del_plots_from_dbfile(db_file, filenames, do_vacuum=True, allow_retries=True,
+ # we're good, return the data:
+ return filename_list, out_dict
+
+[docs]def del_plots_from_dbfile(db_file, filenames, do_vacuum=True, allow_retries=True,
db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS,
- skip_warning=False):
+ skip_warning=False):
'''
deletes a list of files from a database file created by :mod:`ImageMetaTag.db`
@@ -547,97 +549,97 @@ Source code for ImageMetaTag.db
else:
fn_list = filenames
- if db_file is None:
+ if db_file is None:
pass
else:
if not os.path.isfile(db_file) or len(fn_list) == 0:
pass
else:
if allow_retries:
- # split the list of filenames up into appropciately sized chunks, so that concurrent
- # delete commands each have a chance to complete:
- # 200 is arbriatily chosen, but seems to work
+ # split the list of filenames up into appropciately sized chunks, so that concurrent
+ # delete commands each have a chance to complete:
+ # 200 is arbriatily chosen, but seems to work
chunk_size = 200
chunks = __gen_chunk_of_list(fn_list, chunk_size)
for chunk_o_filenames in chunks:
- # within each chunk of files, need to open the db, with time out retries etc:
+ # within each chunk of files, need to open the db, with time out retries etc:
n_tries = 1
- wrote_db = False
+ wrote_db = False
while not wrote_db and n_tries <= db_attempts:
try:
- # open the database
+ # open the database
dbcn, dbcr = open_db_file(db_file, timeout=db_timeout)
- # go through the file chunk, one by one, and delete:
+ # go through the file chunk, one by one, and delete:
for fname in chunk_o_filenames:
try:
- dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
+ dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
except sqlite3.OperationalError as OpErr_file:
- if OpErr_file.message == 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE):
- # the db file exists, but it doesn't have anything in it:
+ if OpErr_file.message == 'no such table: {}'.format(SQLITE_IMG_INFO_TABLE):
+ # the db file exists, but it doesn't have anything in it:
if not skip_warning:
- msg = ('WARNING: Unable to delete file entry "{}" from'
- ' database "{}" as database table is missing')
- print msg.format(fname, db_file)
+ msg = ('WARNING: Unable to delete file entry "{}" from'
+ ' database "{}" as database table is missing')
+ print msg.format(fname, db_file)
return
else:
if not skip_warning:
- # if this fails, print a warning...
- # need to figure out why this happens
- msg = ('WARNING: unable to delete file entry:'
- ' "{}", type "{}" from database')
- print msg.format(fname, type(fname))
+ # if this fails, print a warning...
+ # need to figure out why this happens
+ msg = ('WARNING: unable to delete file entry:'
+ ' "{}", type "{}" from database')
+ print msg.format(fname, type(fname))
dbcn.commit()
- # if we got here, then we're good!
- wrote_db = True
- # finally close (for this chunk)
+ # if we got here, then we're good!
+ wrote_db = True
+ # finally close (for this chunk)
dbcn.close()
except sqlite3.OperationalError as OpErr:
- if 'database is locked' in OpErr.message:
- # database being locked is what the retries and timeouts are for:
- print '%s database timeout deleting from file "%s", %s s' \
+ if 'database is locked' in OpErr.message:
+ # database being locked is what the retries and timeouts are for:
+ print '%s database timeout deleting from file "%s", %s s' \
% (dt_now_str(), db_file, n_tries * db_timeout)
n_tries += 1
else:
- # everything else needs to be reported and raised immediately:
+ # everything else needs to be reported and raised immediately:
raise ValueError(OpErr.message)
- # if we went through all the attempts then it is time to raise the error:
+ # if we went through all the attempts then it is time to raise the error:
if n_tries > db_attempts:
- msg = '{} for file {}'.format(OpErr.message, db_file)
+ msg = '{} for file {}'.format(OpErr.message, db_file)
raise sqlite3.OperationalError(msg)
else:
- # just open the database:
+ # just open the database:
dbcn, dbcr = open_db_file(db_file)
- # delete the contents:
+ # delete the contents:
for i_fn, fname in enumerate(fn_list):
try:
- dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
+ dbcr.execute("DELETE FROM %s WHERE fname=?" % SQLITE_IMG_INFO_TABLE, (fname,))
except:
if not skip_warning:
- # if this fails, print a warning...
- # need to figure out why this happens
- msg = ('WARNING: unable to delete file entry:'
- ' "{}", type "{}" from database')
- print msg.format(fname, type(fname))
- # commit every 100 to give other processes a chance:
+ # if this fails, print a warning...
+ # need to figure out why this happens
+ msg = ('WARNING: unable to delete file entry:'
+ ' "{}", type "{}" from database')
+ print msg.format(fname, type(fname))
+ # commit every 100 to give other processes a chance:
if i_fn % 100 == 0:
dbcn.commit()
time.sleep(1)
- # commit, and vacuum if required:
+ # commit, and vacuum if required:
dbcn.commit()
if do_vacuum:
if allow_retries:
- # need to re-open the db, if we allowed retries:
+ # need to re-open the db, if we allowed retries:
dbcn, dbcr = open_db_file(db_file)
- dbcn.execute("VACUUM")
+ dbcn.execute("VACUUM")
dbcn.close()
elif not allow_retries:
- dbcn.close()
-
+ dbcn.close()
+
def __gen_chunk_of_list(in_list, chunk_size):
- 'gnerator that yields a chunk of list, of length chunk size'
+ 'gnerator that yields a chunk of list, of length chunk size'
for ndx in range(0, len(in_list), chunk_size):
yield in_list[ndx:min(ndx + chunk_size, len(in_list))]
@@ -647,19 +649,19 @@ Source code for ImageMetaTag.db
Returns the output, processed by :func:`ImageMetaTag.db.process_select_star_from`
'''
- if db_file is None:
- sel_results = None
+ if db_file is None:
+ sel_results = None
else:
if not os.path.isfile(db_file):
- sel_results = None
+ sel_results = None
else:
- # just open the database:
+ # just open the database:
dbcn, dbcr = open_db_file(db_file)
- # do the select:
+ # do the select:
sel_results = select_dbcr_by_tags(dbcr, select_tags)
dbcn.close()
- return sel_results
-
+ return sel_results
+
[docs]def select_dbcr_by_tags(dbcr, select_tags):
'''
Selects from an open database cursor (dbcr) the entries that match a dict of field
@@ -668,43 +670,43 @@ Source code for ImageMetaTag.db
Returns the output, processed by :func:`ImageMetaTag.db.process_select_star_from`
'''
if len(select_tags) == 0:
- # just read and return the whole thing:
+ # just read and return the whole thing:
return read_img_info_from_dbcursor(dbcr)
else:
- # convert these to lists:
+ # convert these to lists:
tag_names = select_tags.keys()
tag_values = [select_tags[x] for x in tag_names]
- # Right... this is where I need to understand how to do a select!
- #select_command = 'SELECT * FROM %s WHERE symbol=?' % SQLITE_IMG_INFO_TABLE
- select_command = 'SELECT * FROM %s WHERE ' % SQLITE_IMG_INFO_TABLE
+ # Right... this is where I need to understand how to do a select!
+ #select_command = 'SELECT * FROM %s WHERE symbol=?' % SQLITE_IMG_INFO_TABLE
+ select_command = 'SELECT * FROM %s WHERE ' % SQLITE_IMG_INFO_TABLE
n_tags = len(tag_names)
use_tag_values = []
for i_tag, tag_name, tag_val in zip(range(n_tags), tag_names, tag_values):
if isinstance(tag_val, (list, tuple)):
- # if a list or tuple, then use IN:
- select_command += '%s IN (' % info_key_to_db_name(tag_name)
- select_command += ', '.join(['?']*len(tag_val))
- select_command += ')'
+ # if a list or tuple, then use IN:
+ select_command += '%s IN (' % info_key_to_db_name(tag_name)
+ select_command += ', '.join(['?']*len(tag_val))
+ select_command += ')'
if i_tag+1 < n_tags:
- select_command += ' AND '
+ select_command += ' AND '
use_tag_values.extend(tag_val)
else:
- # do an exact match:
+ # do an exact match:
if i_tag+1 < n_tags:
- select_command += '%s = ? AND ' % info_key_to_db_name(tag_name)
+ select_command += '%s = ? AND ' % info_key_to_db_name(tag_name)
else:
- select_command += '%s = ?' % info_key_to_db_name(tag_name)
+ select_command += '%s = ?' % info_key_to_db_name(tag_name)
use_tag_values.append(tag_val)
db_contents = dbcr.execute(select_command, use_tag_values).fetchall()
- # and convert that to a useful dict/list combo:
+ # and convert that to a useful dict/list combo:
filename_list, out_dict = process_select_star_from(db_contents, dbcr)
- return filename_list, out_dict
-
-[docs]def scan_dir_for_db(basedir, db_file, img_tag_req=None, subdir_excl_list=None,
- known_file_tags=None, verbose=False, no_file_ext=False,
- return_timings=False, restart_db=False):
+ return filename_list, out_dict
+
+[docs]def scan_dir_for_db(basedir, db_file, img_tag_req=None, subdir_excl_list=None,
+ known_file_tags=None, verbose=False, no_file_ext=False,
+ return_timings=False, restart_db=False):
'''
A useful utility that scans a directory on disk for images that can go into a database.
This should only be used to build a database from a directory of tagged images that
@@ -734,10 +736,10 @@ Source code for ImageMetaTag.db
'''
if os.path.isfile(db_file) and not restart_db:
- raise ValueError('''scan_dir_for_db will not work on a pre-existing file unless restart_db
-is True, in which case the database file will be restarted as empty. Use with care.''')
+ raise ValueError('''scan_dir_for_db will not work on a pre-existing file unless restart_db
+is True, in which case the database file will be restarted as empty. Use with care.''')
- if known_file_tags is not None:
+ if known_file_tags is not None:
known_files = known_file_tags.keys()
else:
known_files = []
@@ -745,56 +747,56 @@ Source code for ImageMetaTag.db
if return_timings:
prev_time = datetime.now()
add_interval = 1
- # total number of entries added
+ # total number of entries added
n_added = 0
- # number of entries added since last timer
+ # number of entries added since last timer
n_add_this_timer = 0
- # and this is the list to return:
+ # and this is the list to return:
n_adds = []
timings_per_add = []
os.chdir(basedir)
- first_img = True
- for root, dirs, files in os.walk('./', followlinks=True, topdown=True):
- if not subdir_excl_list is None:
+ first_img = True
+ for root, dirs, files in os.walk('./', followlinks=True, topdown=True):
+ if not subdir_excl_list is None:
dirs[:] = [d for d in dirs if not d in subdir_excl_list]
for meta_img_format in META_IMG_FORMATS:
- for filename in fnmatch.filter(files, '*%s' % meta_img_format):
- # append to the list, taking off the preceeding './' and the file extension:
- if root == './':
+ for filename in fnmatch.filter(files, '*%s' % meta_img_format):
+ # append to the list, taking off the preceeding './' and the file extension:
+ if root == './':
img_path = filename
else:
- img_path = '%s/%s' % (root[2:], filename)
+ img_path = '%s/%s' % (root[2:], filename)
if no_file_ext:
img_name = os.path.splitext(img_path)[0]
else:
img_name = img_path
- # read the metadata:
+ # read the metadata:
if img_name in known_files:
- # if we know this file details, then get it:
+ # if we know this file details, then get it:
known_files.remove(img_name)
img_info = known_file_tags.pop(img_name)
- read_ok = True
+ read_ok = True
else:
- # otherwise read from disk:
+ # otherwise read from disk:
(read_ok, img_info) = readmeta_from_image(img_path)
if read_ok:
if img_tag_req:
- # check to see if an image is needed:
+ # check to see if an image is needed:
use_img = check_for_required_keys(img_info, img_tag_req)
else:
- use_img = True
+ use_img = True
if use_img:
if first_img:
db_cn, db_cr = open_or_create_db_file(db_file, img_info,
- restart_db=True)
- first_img = False
+ restart_db=True)
+ first_img = False
write_img_to_open_db(db_cr, img_name, img_info)
if verbose:
- print img_name
+ print img_name
if return_timings:
n_added += 1
@@ -803,23 +805,23 @@ Source code for ImageMetaTag.db
time_interval_s = (datetime.now()- prev_time).total_seconds()
timings_per_add.append(time_interval_s / add_interval)
n_adds.append(n_added)
- # increase the add_interval so we don't swamp
- # the processing with timings!
+ # increase the add_interval so we don't swamp
+ # the processing with timings!
add_interval = np.ceil(np.sqrt(n_added))
n_add_this_timer = 0
if verbose:
- print 'len(n_adds)=%s, currently every %s' \
+ print 'len(n_adds)=%s, currently every %s' \
% (len(n_adds), add_interval)
- # commit and close, and we are done:
+ # commit and close, and we are done:
if not first_img:
db_cn.commit()
db_cn.close()
if return_timings:
- return n_adds, timings_per_add
-
+ return n_adds, timings_per_add
+
def rmfile(path):
"""
os.remove, but does not complain if the file has already been
@@ -833,8 +835,8 @@ Source code for ImageMetaTag.db
else: raise
def dt_now_str():
- 'returns datetime.now(), as a string, in a common format'
- return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
+ 'returns datetime.now(), as a string, in a common format'
+ return datetime.now().strftime('%Y-%m-%d %H:%M:%S')
@@ -842,7 +844,7 @@ Source code for ImageMetaTag.db
-
+
Navigation
-
@@ -851,13 +853,14 @@
Navigation
-
modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
+ - ImageMetaTag 0.6.5 documentation »
+ - Module code »
+ - ImageMetaTag »
-
- © Copyright 2015-2017, British Crown Copyright.
- Created using Sphinx 1.4.8.
+
+ © Copyright 2015-2017, British Crown Copyright.
+ Created using Sphinx 1.2.2.
\ No newline at end of file
diff --git a/docs/build/html/_modules/ImageMetaTag/img_dict.html b/docs/build/html/_modules/ImageMetaTag/img_dict.html
deleted file mode 100644
index 56be4e4..0000000
--- a/docs/build/html/_modules/ImageMetaTag/img_dict.html
+++ /dev/null
@@ -1,1017 +0,0 @@
-
-
-
-
-
-
-
- ImageMetaTag.img_dict — ImageMetaTag 0.6.4 documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Navigation
-
- -
- index
- -
- modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
-
-
-
-
-
-
- Quick search
-
-
-
-
-
-
-
-
-
-
-
- Source code for ImageMetaTag.img_dict
-'''
-This submodule contains the :class:`ImageMetaTag.ImageDict` class, and functions for preparing them.
-
-The purpose of an :class:`ImageMetaTag.ImageDict` is to sort the image metadata, supplied to
-:func:`ImageMetaTag.savefig` and usually stored in a database file, into a useful form that
-can quickly and easily be presented as a webpage by :func:`ImageMetaTag.webpage.write_full_page`.
-
-An easy example of creating a webpage, using an ImageDict is shown in
-`simplest_image_dict.py <simple.html>`_
-
-.. moduleauthor:: Malcolm Brooks https://github.com/malcolmbrooks
-'''
-# required imports
-import os, re, collections, inspect, copy
-from PIL import Image
-# useful for debugging
-import pdb
-
-from copy import deepcopy
-from itertools import islice, compress
-from math import ceil
-
-[docs]class ImageDict(object):
- '''
- A class which holds a heirachical dictionary of dictionaries, and the associated
- methods for appending/removing dictionaries from it.
-
- The expected use case for the dictionary is to represent a large set of images,
- which can be organised by their metadata tags.
-
- When used in this way, the ImageDict module contains functions to produce web
- pages for browsing the images.
-
- The input_dict should be a heirachical dictionary of dictionaries, containing
- the image metadata, in the required order. In order to convert a flat dictionary
- of metadata items, use :func:`ImageMetaTag.dict_heirachy_from_list`
-
- Options:
- * level_names - a list of the tagnames, in full, giving a name/description of what \
- the metadata item means. Ordered by level of the input dict.
- * selector_widths - a list of html strings giving the widths of each selector in the \
- output webpage. CURRENTLY UNUSED!
- * selector_animated - an integer indicating which selector on the output webpage is \
- to be animated.
- * animation_dicrection - +1 indicates the animation moves forwards, -1 indicates it \
- moves backwards.
-
- Objects:
- * dict - the heirachical dictionary of dictionaries containing the image structure.
- * keys - a list of keys for each level of the dict, within a dictionary using the level \
- number as the keys.
- * level_names - If supplied, this stores the full names of the items in keys.
- * selector_animated - the level index which will be animated on the output webpage.
- * animation_direction - the direction of animation (+1 for forward, -1 for backward).
- * selector_widths - a list of desired widths on the output web page (CURRENTLY UNUSED).
- '''
- def __init__(self, input_dict, level_names=None,
- selector_widths=None, selector_animated=None,
- animation_direction=None):
-
- if level_names is None:
- self.level_names = None
- else:
- if not isinstance(level_names, list):
- msg = 'A mapping of key names to full names has been supplied, but it is not a list'
- raise ValueError(msg)
- else:
- self.level_names = level_names
-
- # set the dictionary:
- self.dict = input_dict
- # now list the keys, at each level, as lists. These can be reorderd by the calling routine,
- # so when the dictionary is written out, they can be in the desired order:
- self.list_keys_by_depth()
-
- dict_depth = self.dict_depth()
- if self.level_names is not None:
- if dict_depth != len(self.level_names):
- raise ValueError('Mismatch between depth of dictionary and level_names')
-
- # this controls the width of the selector, on the resultant webpage:
- if selector_widths is None:
- self.selector_widths = [""] * dict_depth
- else:
- if not isinstance(selector_widths, list):
- msg = 'Specified selector_widths should be a list, but it is '
- msg += '"%s" instead' % selector_widths
- raise ValueError(msg)
- if len(selector_widths) != self.dict_depth():
- msg = 'Specified selector_widths length disagrees with the dictionary depth'
- raise ValueError(msg)
-
- self.selector_widths = selector_widths
- # this controls whether or not a particualr selector uses the animation controls or not.
- # There can be only one per page, default is that none use it...
- if selector_animated is None:
- self.selector_animated = -1
- else:
- if not isinstance(selector_animated, int):
- msg = 'Specified selector_animated should be a single integer'
- raise ValueError(msg)
- if selector_animated < -1 or selector_animated >= self.dict_depth():
- msg = 'selector_animated (%s)' % selector_animated
- msg += ' is out of range of the plot dictionary depth.'
- raise ValueError(msg)
- # that passes, so store it:
- self.selector_animated = selector_animated
-
- if animation_direction is None:
- self.animation_direction = 1
- else:
- if not isinstance(animation_direction, int) or \
- (animation_direction != 1 and animation_direction != -1):
- msg = 'Specified animation_direction should be a single integer with values'
- msg += ' +1 or -1, but it is "%s" instead' % animation_direction
- raise ValueError(msg)
- self.animation_direction = animation_direction
-
- def __repr__(self):
- outstr = 'ImageMetaTag ImageDict:\n'
- outstr = self.dict_print(self.dict, indent=1, outstr=outstr)
- return outstr
-
-[docs] def append(self, new_dict, devmode=False, skip_key_relist=False):
- '''
- appends a new dictionary (with a single element in each layer!) into a current ImageDict.
-
- The skip_key_relist option can be set to True to stop the regeneration of key lists.
- '''
- if isinstance(new_dict, ImageDict):
- merged_dict = dict(self.mergedicts(self.dict, new_dict.dict))
- self.dict = merged_dict
- elif isinstance(new_dict, dict):
- merged_dict = dict(self.mergedicts(self.dict, new_dict))
- self.dict = merged_dict
- else:
- raise ValueError('Cannot append data type %s to a ImageDict' % type(new_dict))
-
- if not skip_key_relist:
- self.list_keys_by_depth(devmode=devmode)
-
- # if there is a level_names, check that the
- # new dict is consistent:
- if isinstance(new_dict, ImageDict) and self.level_names is not None:
- if new_dict.level_names is not None:
- if self.level_names != new_dict.level_names:
- raise ValueError('Attempting to append two ImageDict objects with'
- ' different level_names')
-
-[docs] def dict_union(self, in_dict, new_dict):
- 'produces the union of a dictionary of dictionaries'
- for key, val in new_dict.iteritems():
- if not isinstance(val, dict):
- in_dict[key] = val
- else:
- subdict = in_dict.setdefault(key, {})
- self.dict_union(subdict, val)
-
-[docs] def mergedicts(self, dict1, dict2):
- '''
- Alternative version of dict_union using generators which is much faster for large dicts
- but needs to be converted to a dict when it's called:
- new_dict = dict(mergdicts(dict1,dict))
- '''
- for k in set(dict1.keys()).union(dict2.keys()):
- if k in dict1 and k in dict2:
- if isinstance(dict1[k], dict) and isinstance(dict2[k], dict):
- yield (k, dict(self.mergedicts(dict1[k], dict2[k])))
- else:
- # If one of the values is not a dict, you can't continue merging it.
- # Value from second dict overrides one in first and we move on.
- yield (k, dict2[k])
- # Alternatively, replace this with exception raise
- # to alert you of value conflicts
- elif k in dict1:
- yield (k, dict1[k])
- else:
- yield (k, dict2[k])
-
-[docs] def remove(self, rm_dict, skip_key_relist=False):
- '''
- removes a dictionary from within an ImageDict.
- The skip_key_relist option can be set to True to stop the regeneration of key lists.
-
- TIP: Because the remove process needs to prune empty sections afterwards,
- it can be slow. When working with large dictionaries, and removing a large number
- of elements from it, is often faster to build up a dictionary of things you want to
- remove, and then do one remove at the end.
- '''
- # delete the items in question from the dictionary - this can leave
- # empty branches of the dictionary though:
- if isinstance(rm_dict, ImageDict):
- self.dict_remove(self.dict, rm_dict.dict)
- elif isinstance(rm_dict, dict):
- self.dict_remove(self.dict, rm_dict)
- else:
- raise ValueError('Cannot remove data type %s from a ImageDict' % type(rm_dict))
- # now prune empty branches away:
- dicts_to_prune = True
- while dicts_to_prune:
- dicts_to_prune = self.dict_prune(self.dict)
- # relist the keys:
- if not skip_key_relist:
- self.list_keys_by_depth()
-
-[docs] def dict_remove(self, in_dict, rm_dict):
- '''
- removes a dictionary of dictionaries from another, larger, one.
- This can leave empty branches, at multiple levels of the dict,
- so needs cleaning up afterwards.
- '''
- for key, val in rm_dict.iteritems():
- if isinstance(val, dict):
- # descend further up into the dictionary tree structure:
- self.dict_remove(in_dict.setdefault(key, {}), val)
- else:
- # if the key is in the in_dict at this level, remove it:
- if key in in_dict.keys():
- in_dict.pop(key)
-
-[docs] def dict_prune(self, in_dict, dicts_pruned=False):
- '''
- Prunes the ImageDict of empty, unterminated, branches
- (which occur after parts have been removed).
- Returns True if a dict was pruned, False if not.
- '''
- pop_list = []
- for key, val in in_dict.iteritems():
- if isinstance(val, dict):
- # descend further up into the dictionary tree structure:
- if len(val.keys()) > 0:
- dicts_pruned = self.dict_prune(val, dicts_pruned=dicts_pruned)
- else:
- pop_list.append(key)
- dicts_pruned = True
- elif val is None:
- pop_list.append(key)
- dicts_pruned = True
- # now do the prune:
- for key in pop_list:
- in_dict.pop(key)
-
- return dicts_pruned
-
-[docs] def dict_print(self, in_dict, indent=0, outstr=''):
- 'recursively details a dictionary of dictionaries, with indentation, to a string'
- n_spaces = 2 # number of spaces to indent, per level
- # loop over the defined list of keys, to present in them in the current sorted order.
- for key, value in in_dict.iteritems():
- outstr = '%s%s%s:\n' % (outstr, ' '*indent*n_spaces, key)
- if isinstance(value, dict):
- outstr = self.dict_print(value, indent=indent+1, outstr=outstr)
- else:
- outstr = '%s%s%s\n' % (outstr, ' '*(indent+1)*n_spaces, value)
- return outstr
-
-[docs] def dict_depth(self, uniform_depth=False):
- '''
- Uses dict_depths to get the depth of all branches of the plot_dict and,
- if required, checks they all equal the max
- '''
- # get the depth of all trees of the dict, and flatten the list:
- dict_depths = [d for d in self.flatten_lists(self.dict_depths(self.dict))]
- # find the max:
- dict_depth = max(dict_depths)
- # and check its uniformity if required:
- if uniform_depth:
- # check the dictionary depth is uniform_depth for all elements,
- # and raise an error if not
- if len(set(dict_depths)) != 1:
- msg = 'Plot Dictionary has non uniform depth and uniform_depth=True is specified'
- raise ValueError(msg)
- return dict_depth
-
-[docs] def dict_depths(self, in_dict, depth=0):
- 'Recursively finds the depth of a ImageDict and returns a list of lists'
- if not isinstance(in_dict, dict) or not in_dict:
- return depth
- return [self.dict_depths(next_dict, depth+1) for (next_key, next_dict) in in_dict.iteritems()]
-
-[docs] def flatten_lists(self, in_list):
- 'Recursively flattens a list of lists:'
- for list_element in in_list:
- if isinstance(list_element, collections.Iterable) and \
- not isinstance(list_element, basestring):
- for sub_list in self.flatten_lists(list_element):
- yield sub_list
- else:
- yield list_element
-
-[docs] def list_keys_by_depth(self, devmode=False):
- '''
- Lists the keys of the dictionary to create a list of keys, for each level of the
- dictionary, up to its depth.
-
- It is usually much faster to create an ImageDict by appending images to it,
- with skip_key_relist=True but this leaves an ImageDict without a list of keys. In this
- case, list_keys_by_depth needs to be called once at the end of the process to complete
- the list of keys.
-
- It works by converting the sets, from keys_by_depth to a list (where they can be ordered
- and indexed).
- This also produces the unique subdirectory locations of all images.
- '''
- keys, subdirs = self.keys_by_depth(self.dict)
-
- out_keys = {}
- for level in keys.keys():
- # convert to a sorted list:
- out_keys[level] = sorted(list(keys[level]))
-
- self.keys = out_keys
- self.subdirs = sorted(list(subdirs))
- #return out_keys
-
-[docs] def keys_by_depth(self, in_dict, depth=0, keys=None, subdirs=None):
- '''
- Returns:
-
- * a dictionary of sets, containing the keys at each level of\
- the dictionary (keyed by the level number).
- * a set of the subdirectories for the target images
- '''
- if keys is None:
- keys = {}
- subdirs = set([])
- if not depth in keys:
- keys[depth] = set()
- for key in in_dict:
- keys[depth].add(key)
- if isinstance(in_dict[key], dict):
- self.keys_by_depth(in_dict[key], depth+1, keys, subdirs)
- elif isinstance(in_dict[key], list):
- # we have a list of images:
- for img_file in in_dict[key]:
- subdirs.add(os.path.split(img_file)[0])
- elif isinstance(in_dict[key], str):
- # we have the location of a single image;
- subdirs.add(os.path.split(in_dict[key])[0])
- return keys, subdirs
-
-[docs] def key_at_depth(self, in_dict, depth):
- 'returns the keys of a dictionary, at a given depth'
- if depth > 0:
- return [key for subdict in in_dict.itervalues() for key in self.key_at_depth(subdict, depth-1)]
- else:
- return in_dict.keys()
-
-[docs] def return_key_inds(self, in_dict, out_array=None, this_set_of_inds=None,
- depth=None, level=None, verbose=False, devmode=False):
- '''
- Does the work for dict_index_array, by recursively adding indices to
- the keys to a current list, and branching where required, and adding
- compelted lists to the out_array
- '''
- for key, value in in_dict.iteritems():
- if verbose:
- print 'IN: level: %s, before changes: %s, key "%s" in %s' \
- % (level, this_set_of_inds, key, self.keys[level])
-
- if isinstance(value, dict):
- # make a note of which key it is:
-
- if key in self.keys[level]:
- # we've moved up a level from the previous one, make a
- # note of the new value at the new level
- this_set_of_inds[level] = self.keys[level].index(key)
- # increment the level, in case the next dict is at the
- # higher level in the tree structure:
- if level+1 < depth:
- level += 1
- if verbose:
- print 'new setting: %s' % this_set_of_inds
- print 'out_array: %s' % out_array
- # and recurse, to the next level if needed:
- self.return_key_inds(value, out_array=out_array,
- this_set_of_inds=this_set_of_inds,
- depth=depth, level=level, devmode=devmode)
- else:
- out_array.append(deepcopy(this_set_of_inds))
-
- elif key in self.keys[level-1]:
- # the dictionary we've now got isn't at a higher level than before, which means
- # we're traversing the level from the previous call.
- # record the new index on a copy, and then recursively carry on:
- branched_level = deepcopy(level)
- branched_set_of_inds = deepcopy(this_set_of_inds)
- branched_set_of_inds[branched_level-1] = self.keys[branched_level-1].index(key)
- if verbose:
- print 'new setting: %s' % this_set_of_inds
- print 'out_array: %s' % out_array
- # and recurse:
- self.return_key_inds(value, out_array=out_array,
- this_set_of_inds=branched_set_of_inds,
- depth=depth, level=branched_level,
- devmode=devmode)
-
- else:
- # we really shouldn't be here:
- msg = 'Error recursing through dict: key "%s"' % key
- msg += ' not found in this level, or one below'
- raise ValueError(msg)
- #elif isinstance(value, dict) and level == depth:
- #
- # pdb.set_trace()
- else:
- # we're at the top level of the tree, as far as we can or want to go,
- # so record the index:
- if key in self.keys[level]:
- # we've moved up a level from the previous one,
- # make a note of the new value at the new level
- this_set_of_inds[level] = self.keys[level].index(key)
- #elif level == 0:
- # # wer're traversing the top level of the dictionary, so
- # # level has been set to zero:
- # this_set_of_inds[-1] = self.keys[depth-1].index(key)
- else:
- # again, we shouldn't ever get here:
- if devmode:
- pdb.set_trace() #@@@
- print 'stop here' #@@@
- else:
- msg = 'Error recursing through the plot dictionary: '
- msg += 'key not found in top level!'
- raise ValueError(msg)
-
- # we're done, so append this to the out_array, and DON'T recurse:
- out_array.append(deepcopy(this_set_of_inds))
-
-[docs] def dict_index_array(self, devmode=False, maxdepth=None, verbose=False):
- '''
- Using the list of dictionary keys (at each level of a uniform_depth dictionary
- of dictionaries), this produces a list of the indices that can be used to reference
- the keys to get the result for each element.
-
- Options:
- * maxdepth - the maximum desired depth to go to (ie. the number of levels)
- '''
- # want to build up a list of lists, each giving the indices of the keys that get to a plot:
- out_array = []
- if maxdepth is None:
- depth = self.dict_depth(uniform_depth=True)
- this_set_of_inds = [None] * depth
- else:
- depth=maxdepth
- this_set_of_inds = [None] * depth
- level = 0
- self.return_key_inds(self.dict, out_array=out_array,
- this_set_of_inds=this_set_of_inds, depth=depth,
- level=level, devmode=devmode, verbose=verbose)
- # the recursive method comes out sorted as it iterates about the dictionary,
- # not as to how the keys are sorted. Easy to do:
- out_array.sort()
-
- return (self.keys, out_array)
-
-[docs] def sort_keys(self, sort_methods, devmode=False):
- '''
- Sorts the keys of a plot dictionary, according to a particular sort method
- (or a list of sort methods that matches the number of keys).
-
- Valid sort methods so far are mostly focused on meteorological terms, and include:
- * 'sort' - just an ordinary sort
- * 'level' or 'numeric' - starting with the surface and working upwards, then \
- 'special' levels like cross sections etc.
- * 'T+' - in ascending order of the number after a T+ (or similar) string.
- * an input list - Specific elements in the input list are sorted as per their order\
- in the list, while the rest are just sorted.
-
- The methods activated by a string can be reversed as 'reverse_sort' or 'reverse_sort',
- or 'reverse numeric' or 'reverse_numeric'.
- '''
-
- if len(sort_methods) != len(self.keys):
- raise ValueError('inconsistent lengths of the sort_methods and the self.keys to sort')
-
- for i_key, method in enumerate(sort_methods):
- if isinstance(method, str):
- # the method is a specified way of sorting:
- if method in ['sort', 'alphabetical']:
- # - just alphanumeric style sort.
- self.keys[i_key].sort()
-
- elif method in ['reverse sort', 'reverse_sort']:
- # -a reversed sort
- self.keys[i_key].sort(reverse=True)
-
- elif method in ['T+', 'reverse T+', 'reversed_T+']:
- #'T+' - in ascending order of the T+??? number:
-
- # get a list of tuples, containing the string, and the value
- # of the T+ from a pattern match regex:
- try:
- labels_and_values = [(x, re.match('[tT][+]{,}([0-9.]{1,})|None', x).group(1)) for x in self.keys[i_key]]
- if method == 'T+':
- # map the None to a string, so it goes to then end of a sort
- labels_and_values = [(x, float(y)) if not y is None else (x, 'None') for x, y in labels_and_values]
- elif method in ['reverse T+', 'reversed_T+']:
- # don;t map the none to a string, so it goes to the
- # end of a reversed sort...
- labels_and_values = [(x, float(y)) if not y is None else (x, y) for x, y in labels_and_values]
- except:
- msg = 'Keys for plot dictionary level "%s" ' % self.keys[i_key]
- msg += 'do not match the "T+" (or None) pattern'
- if devmode:
- print msg
- pdb.set_trace()
- print 'stop'
- else:
- raise ValueError(msg)
-
- # now either sort, or reverse sort, using the value as the key:
- if method == 'T+':
- labels_and_values.sort(key=lambda x: x[1])
- elif method in ['reverse T+', 'reversed_T+']:
- labels_and_values.sort(key=lambda x: x[1], reverse=True)
- # and pull out the labels, in the right order:
- self.keys[i_key] = [x[0] for x in labels_and_values]
-
- elif method in ['level', 'numeric', 'reverse_level', 'reverse_numeric']:
- #'level' - starting with the surface and working upwards, then 'special'
- # levels like cross sections etc.
-
- # add to this as keys from self.keys[i_key] are added to it:
- tmp_keys = []
-
- # the surface levels go first:
- surface_levels = ['Surface']
- for item in surface_levels:
- if item in self.keys[i_key]:
- tmp_keys.append(self.keys[i_key].pop(self.keys[i_key].index(item)))
- if len(self.keys[i_key]) == 0:
- break
-
- # now anything with a 'm' or 'km' or 'nm' (for wavelenghts!) needs
- # to be sorted, starting with the lowest:
- # TODO: add more things to this, microns, with the micro as /mu????
- metre_patterns_and_scalings = [(r'([0-9.eE+-]{1,})[\s]{,}m$', 1.0),
- (r'([0-9.eE+-]{1,})[\s]{,}mm$', 1.0-3),
- (r'([0-9.eE+-]{1,})[\s]{,}microns$', 1.0-6),
- (r'([0-9.eE+-]{1,})[\s]{,}\mum$', 1.0-6),
- (r'([0-9.eE+-]{1,})[\s]{,}nm$', 1.0e-9),
- (r'([0-9.eE+-]{1,})[\s]{,}km$', 1000.0)]
-
- # now anything with a 'hPa' or 'mb' needs to be sorted, starting
- # with the lowest in height (hieghest value):
- pressure_patterns_and_scalings = [(r'([0-9.eE+-]{1,})[\s]{,}Pa$', 1.0),
- (r'([0-9.eE+-]{1,})[\s]{,}mb$', 100.0),
- (r'([0-9.eE+-]{1,})[\s]{,}mbar$', 100.0),
- (r'([0-9.eE+-]{1,})[\s]{,}hPa$', 100.0)]
-
- model_lev_patterns_and_scalings = [(r'Model level ([0-9]{1,})', 1.0),
- (r'model level ([0-9]{1,})', 1.0),
- (r'Model lev ([0-9]{1,})', 1.0),
- (r'model level ([0-9]{1,})', 1.0),
- (r'ML([0-9]{1,})', 1.0),
- (r'ml([0-9]{1,})', 1.0)]
-
- # now anything where the level defines locations, with latt long coordinates:
- lattlong_patterns_and_scalings = [('([0-9.]{1,})[E][,\s]{,}[0-9.]{1,}[NS]', 1.0),
- ('([0-9.]{1,})[W][,\s]{,}[0-9.]{1,}[NS]', -1.0)]
-
- # now anything else with a numeric value:
- numeric_patterns_and_scalings = [(r'([0-9.eE+-]{1,})', 1.0)]
-
- # now loop through the different patterns/scalings, and their orders:
- if method.startswith('reverse'):
- pattern_order_loop = [(metre_patterns_and_scalings, 'reversed'),
- (pressure_patterns_and_scalings, 'sort'),
- (model_lev_patterns_and_scalings, 'reversed'),
- (lattlong_patterns_and_scalings, 'reversed'),
- (numeric_patterns_and_scalings, 'reversed')]
- else:
- pattern_order_loop = [(metre_patterns_and_scalings, 'sort'),
- (pressure_patterns_and_scalings, 'reversed'),
- (model_lev_patterns_and_scalings, 'sort'),
- (lattlong_patterns_and_scalings, 'sort'),
- (numeric_patterns_and_scalings, 'sort')]
-
- for patterns_scalings, sort_method in pattern_order_loop:
- labels_and_values = []
- for item in self.keys[i_key]:
- for pattern, scaling in patterns_scalings:
- item_match = re.match(pattern, item)
- if item_match:
- # add the label and value to the list:
- if devmode:
- try:
- _ = float(item_match.group(1)) * scaling
- except:
- pdb.set_trace()
- print 'unable to convert item "%s" with pattern "%s" to float' % (item, pattern)
- labels_and_values.append((item, float(item_match.group(1)) * scaling))
- break
- # now sort the labels_and_values, according to the value:
- if sort_method == 'sort':
- labels_and_values.sort(key=lambda x: x[1])
- elif sort_method == 'reversed':
- labels_and_values.sort(key=lambda x: x[1], reverse=True)
- else:
- raise ValueError('Unrecognised sort_method "%s"' % sort_method)
- # add the labels to the tmp_keys and remove them from the self.keys[i_key]:
- for item, scaling in labels_and_values:
- tmp_keys.append(item)
- self.keys[i_key].pop(self.keys[i_key].index(item))
-
-
- # now sort what's left:
- self.keys[i_key].sort()
-
- # and put the tmp_keys at the start of it:
- self.keys[i_key] = tmp_keys + self.keys[i_key]
-
-
- elif isinstance(method, list):
- # the input list should be a list of strings, which give the priority contents
- # to be put at the start of the list. The remaining items are sorted normally:
- # - specific model names get to the top, the rest are alphaebetical
-
- # add to tmp_keys as we go, by popping elements from the main list:
- tmp_keys = []
- # this sets the order of the model names (names not in this list are alphabetical):
- for item in method:
- if item in self.keys[i_key]:
- tmp_keys.append(self.keys[i_key].pop(self.keys[i_key].index(item)))
- if len(self.keys[i_key]) == 0:
- break
- # now sort the remaining keys alphabetically:
- self.keys[i_key].sort()
- # and put the tmp_keys back in at the start:
- self.keys[i_key] = tmp_keys + self.keys[i_key]
-
-[docs] def copy_except_dict_and_keys(self):
- 'returns a copy of an ImageDict except it will have null values for the dict and keys'
- out_imgdict = ImageDict({'null': None})
- for mem_name, mem_value in inspect.getmembers(self):
- if mem_name.startswith('__'):
- pass
- elif mem_name in ['dict', 'keys']:
- pass
- elif inspect.ismethod(eval('self.%s' % mem_name)):
- pass
- else:
- setattr(out_imgdict, mem_name, mem_value)
-
- return out_imgdict
-
-[docs] def return_from_list(self, vals_at_depth):
- '''
- Returns the end values of ImageDict, when given a list of values for the keys
- at different depths.
- Returns None if the set of values is not contained in the ImageDict.
-
- Assumes that the ImageDict has its keys up to date, so last time it was
- appended/removed it was with skip_key_relist=False or list_keys_by_depth()
- method has been called since.
- '''
-
- if not isinstance(vals_at_depth, list):
- raise ValueError('Input vals_at_depth should be a list')
- dict_depth = len(self.keys)
- if len(vals_at_depth) > dict_depth:
- msg = 'Length of input list, vals_at_depth, greater than the length of the keys list'
- raise ValueError(msg)
-
- if vals_at_depth[0] not in self.keys[0]:
- return None
- else:
- sub_dict = self.dict[vals_at_depth[0]]
-
- for depth in range(1, len(vals_at_depth)):
- if vals_at_depth[depth] in sub_dict:
- sub_dict = sub_dict[vals_at_depth[depth]]
- else:
- return None
- return sub_dict
-
-
-[docs]def readmeta_from_image(img_file, img_format=None):
- '''Reads the metadata added by the ImageMetaTag savefig, from an image file,
- and returns a dictionary of *tagname: value* pairs'''
-
- if img_format is None:
- # get the img_format from the end of the filename
- _, img_format = os.path.splitext(img_file)
- if img_format is None or img_format == '':
- msg = 'Cannot determine file img_format to read from filename "%s"' % img_file
- raise ValueError(msg)
- # get rid of the . to be consistent throughout
- img_format = img_format[1:]
- else:
- if img_format.startswith('.'):
- img_format = img_format[1:]
-
- # how we read in the metadata depends on the format:
- if img_format == 'png':
- try:
- img_obj = Image.open(img_file)
- img_info = img_obj.info
- read_ok = True
- except:
- # if anthing goes wrong, then read_ok is False and img_info None
- read_ok = False
- img_info = None
- else:
- msg = 'Currently, ImageMetaTag does not support "%s" format images' % img_format
- raise NotImplementedError(msg)
-
- return (read_ok, img_info)
-
-[docs]def dict_heirachy_from_list(in_dict, payload, heirachy):
- '''
- Converts a flat dictionary of *tagname: value* pairs, into an ordered dictionary
- of dictionaries according to the input heirachy (which is a list of tagnames).
-
- The output dictionary will only have one element per level, but can be used to create or
- append into an :class:`ImageMetaTag.ImageDict`.
- The final level will be the 'payload' input, which is the object the dictionary, with all
- it's levels, is describing.
- The payload would usually be the full/relative path of the image file, or list of image files.
-
- Returns False if the input dict does not contain the required keys.
- '''
- for level in heirachy:
- if not level in in_dict.keys():
- return False
-
- out_dict = {in_dict[heirachy[-1]]: payload}
- for level in heirachy[-2::-1]:
- out_dict = {in_dict[level]: out_dict}
- return out_dict
-
-
-[docs]def dict_split(in_dict, n_split=None, size_split=None, extra_opts=None):
- '''
- Generator that breaks up a flat dictionary and yields a set of sub-dictionaries in
- n_split chunks, or size_split in size. It is split on it's first level, not recursively.
-
- It is very useful for splitting large dictionaries of image metadata
- to parallelise processing these into ImageDicts.
-
- Inputs:
- in_dict - the dictionary to split
-
- Options:
-
- * n_split - the number of dictionaries to break the in_dict up into.
- * size_split - the size of the required output dictionaries.
- * extra_opts - If supplied as an iterable, this routine will yield a tuple containing the \
- output sub-dictionary and then each of the elements of extra_opts.
-
- .. note:: One, and only one, of n_slpit, or size_split must be specified, as an integer.
-
- '''
-
- if not isinstance(in_dict, dict):
- raise ValueError('Input in_dict is not a dictionary')
-
- if len(in_dict) == 0:
- # an empty dict needs to output an empty dict, possiblty with the
- # extra options:
- out_dict = {}
- if extra_opts is None:
- yield out_dict
- else:
- # yield a tuple containing the output dictionary, plus all the extra options,
- # as elements in a tuple:
- out_tuple = (out_dict, )
- for opt in extra_opts:
- out_tuple = out_tuple + (opt,)
- yield out_tuple
- else:
- if size_split is None and isinstance(n_split, int):
- if n_split < 1:
- raise ValueError('Cannot split a dictionary into less than 1 dictionary')
- # work out the size of a split - the last dict can be smaller than the others:
- size_split = int(ceil(len(in_dict) / float(n_split)))
- elif isinstance(size_split, int) and n_split is None:
- # do nothing, use the input size_split
- pass
- else:
- msg = 'One, and only one, of n_slpit, or size_split must be specified, as an integer.'
- raise ValueError(msg)
-
- iterdict = iter(in_dict)
- for i in xrange(0, len(in_dict), size_split):
-
- out_dict = {k:in_dict[k] for k in islice(iterdict, size_split)}
-
- if extra_opts is None:
- yield out_dict
- else:
- # yield a tuple containing the output dictionary, plus all the extra options,
- # as elements in a tuple:
- out_tuple = (out_dict, )
- for opt in extra_opts:
- out_tuple = out_tuple + (opt,)
-
- yield out_tuple
-
-
-
-[docs]def simple_dict_filter(simple_dict, tests, raise_key_mismatch=False):
- '''
- Tests the contents of a simple, un-heirachical dict (properties an image) against
- a set of tests.
-
- An example set of tests:
- tests = {'number of rolls': ['6 simulated rolls', '216 simulated rolls', '1296 simulated rolls'],\
- 'plot color': None,\
- 'image compression': None,\
- 'plot type': ['Histogram', ('All', ['Histogram', 'Line plots'])],\
- 'image trim': None}
- Here, the 'number of rolls' is restricted to a simple list.
-
- The plot type is filtered according to 'Histogram', and there is also a second element that
- contains both 'Histogram and 'Line plots' which are to be presented together.
-
- The other image characteristics are not filtered.
-
- Options:
-
- * raise_key_mismatch - if True, then attempting to test a dictionary with a missing key \
- will raise. Default is to return all False
-
- Returns three logicals:
-
- * The first indicates whether the input dict passes the simple tests
- * The second indicates whether the input dict is part of the grouped elements \
- of the test (the ['Histogram', 'Line plots'] list).#
- * The third indicates whether the input dict is the first element of a list grouped \
- elements (is 'Histogram' in this ['Histogram', 'Line plots'] list).
-
- '''
- # this is set to False if the input dict fails the simple criteria:
- passes_tests = True
- # this stores all of the tests:
- passes = [True] * len(tests)
- # this stores if it passes and if the test is complex, this image is first:
- passes_and_first = [True] * len(tests)
-
- # the input tests can also contain a tuple that define how multiple
- # images can be grouped together:
- has_complex_test = False
- if not tests is None:
- for i_test, test in enumerate(tests.keys()):
- if tests[test] is None:
- # None here means no filter is applied:
- pass
- else:
- if not test in simple_dict.keys():
- msg = 'Specified filter test "%s" not a property of the input dict "%s"' \
- %(test, simple_dict)
- if raise_key_mismatch:
- raise ValueError(msg)
- else:
- print msg
- return (False, False, False)
- if isinstance(tests[test], list):
- # simple test, does it meet the normal criteria:
- # now check to see if the test includes a tuple, marking out a complex
- # multi-element list:
- test_is_tuple = [isinstance(x, tuple) for x in tests[test]]
- if any(test_is_tuple):
- has_complex_test = True
-
- # now loop through all of the tuples within the test:
- any_tuple_passes = False
- # this marks whether it is the FIRST element in a tuple (and so the one that
- # would typically be used for processing a set of images)
- first_tuple_passes = False
- tuple_tests = compress(tests[test], test_is_tuple)
- for tuple_test in tuple_tests:
- if simple_dict[test] in tuple_test[1]:
- # failed this test, so it counts as a failure:
- any_tuple_passes = True
- # and is this the first element in the tuple:
- if simple_dict[test] == tuple_test[1][0]:
- first_tuple_passes = True
-
- # if none of the tuple tests pass, then make a note of that:
- if not any_tuple_passes:
- passes[i_test] = False
- passes_and_first[i_test] = False
- elif not first_tuple_passes:
- # and make a note if this wasn't first:
- passes_and_first[i_test] = False
-
- # also apply a test in the simple case:
- if not simple_dict[test] in tests[test]:
- passes_tests = False
- else:
- # no complex test for this test, just the simlpe one:
- if not simple_dict[test] in tests[test]:
- passes_tests = False
- passes[i_test] = False
- passes_and_first[i_test] = False
-
- else:
- msg = 'Test values should be specified as lists'
- raise ValueError(msg)
-
- if has_complex_test:
- # for the complex test, all tests need to be passed as well as the complex case:
- passes_complex_test = all(passes)
- passes_and_first = all(passes_and_first)
- else:
- passes_complex_test = False
- passes_and_first = False
-
- return (passes_tests, passes_complex_test, passes_and_first)
-
-
-[docs]def check_for_required_keys(img_info, req_keys):
- '''
- Checks an img_info dictionary has a set of required keys, specifed as a list of strings
-
- Returns True or False accordingly.
- '''
- for key in req_keys:
- if key not in img_info.keys():
- return False
- return True
-
-
-
-
-
-
-
-
- Navigation
-
- -
- index
- -
- modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
-
-
-
- © Copyright 2015-2017, British Crown Copyright.
- Created using Sphinx 1.4.8.
-
-
-
\ No newline at end of file
diff --git a/docs/build/html/_modules/ImageMetaTag/savefig.html b/docs/build/html/_modules/ImageMetaTag/savefig.html
deleted file mode 100644
index df9221f..0000000
--- a/docs/build/html/_modules/ImageMetaTag/savefig.html
+++ /dev/null
@@ -1,589 +0,0 @@
-
-
-
-
-
-
-
- ImageMetaTag.savefig — ImageMetaTag 0.6.4 documentation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Navigation
-
- -
- index
- -
- modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
-
-
-
-
-
-
- Quick search
-
-
-
-
-
-
-
-
-
-
-
- Source code for ImageMetaTag.savefig
-'''
-This module contains a wrapper for matplotlib.pyplot.savefig. The primary function of the wrapper
-is to add image metadata taggging and database storage of that metadata.
-
-As the output images are already being post-processed to add the metadata, basic image
-manipulation options are included to crop images, add logos and reduce their file size
-by simplifying their colour palette.
-
-.. moduleauthor:: Malcolm Brooks https://github.com/malcolmbrooks
-'''
-
-import os, sys, io, sqlite3, pdb
-import matplotlib.pyplot as plt
-from datetime import datetime
-
-from ImageMetaTag import db, META_IMG_FORMATS, POSTPROC_IMG_FORMATS
-from ImageMetaTag import DEFAULT_DB_TIMEOUT, DEFAULT_DB_ATTEMPTS
-
-# image manipulations:
-from PIL import Image, ImageChops, PngImagePlugin#, ImageFilter
-import numpy as np
-
-THUMB_DEFAULT_IMG_SIZE = 150, 150
-THUMB_DEFAULT_DIR_NAME = 'thumbnail'
-
-[docs]def savefig(filename, img_format=None, img_converter=0, do_trim=False, trim_border=0,
- do_thumb=False, img_tags=None, keep_open=False, dpi=None,
- logo_file=None, logo_width=40, logo_padding=0, logo_pos=0,
- db_file=None, db_timeout=DEFAULT_DB_TIMEOUT, db_attempts=DEFAULT_DB_ATTEMPTS,
- db_full_paths=False,
- verbose=False, ):
- '''
- A wrapper around matplotlib.pyplot.savefig, to include file size optimisation and
- image tagging.
-
- The filesize optimisation depends on the img_converter input passes into
- :func:`ImageMetaTag.image_file_postproc`.
-
- Args:
- filename (can include the file extension, or that can be specified in the img_format option)
-
- Options:
-
- * img_format - file format of the image. If not supplied it will be guessed from the filename.\
- Currently only the png file format is supported for tagging/conversion.
- * img_tags - a dictionary of {tag_name : value} pairs to be added to the image metadata.
- * db_file - a database file to be used by :func:`ImageMetaTag.db.write_img_to_dbfile` to \
- store all image metadata so they can be quickly accessed.
- * db_full_paths - by default, if the images can be expressed as relative path to the database \
- file then the database will contain only relative links, unless \
- db_full_paths is True.
- * db_timeout - change the database timeout (in seconds).
- * db_attempts - change the number of attempts to write to the database.
- * dpi - change the image resolution passed into matplotlib.savefig.
- * keep_open - by default, this savefig wrapper closes the figure after use, except if \
- keep_open is True.
- * verbose - switch for verbose output (reports file sizes before/after conversion)
- * img_converter - see :func:`ImageMetaTag.image_file_postproc`.
- * do_trim - see :func:`ImageMetaTag.image_file_postproc`.
- * trim_border - see :func:`ImageMetaTag.image_file_postproc`.
- * logo_file - see :func:`ImageMetaTag.image_file_postproc`.
- * logo_width - see :func:`ImageMetaTag.image_file_postproc`.
- * logo_padding - see :func:`ImageMetaTag.image_file_postproc`.
- * logo_pos - see :func:`ImageMetaTag.image_file_postproc`.
- * do_thumb - see :func:`ImageMetaTag.image_file_postproc`.
-
- TODO: the logo would also be good if it could accept a list of files, widths,
- positions and paddings. That way different logos could be added to the top left and top
- right corner, for instance.
-
- '''
-
- if img_format is None:
- write_file = filename
- # get the img_format from the end of the filename
- _, img_format = os.path.splitext(filename)
- if img_format is None or img_format == '':
- msg = 'Cannot determine file img_format to save from filename "%s"' % filename
- raise ValueError(msg)
-
- # get rid of the . to be consistent throughout
- img_format = img_format[1:]
- else:
- if img_format.startswith('.'):
- img_format = img_format[1:]
- write_file = '%s.%s' % (filename, img_format)
-
-
- # Where to save the figure to? If we're going to postprocess it, save to memory
- # for speed and to cut down on IO load:
- do_any_postproc = img_format in META_IMG_FORMATS or img_format in POSTPROC_IMG_FORMATS
- if do_any_postproc:
- buf = io.BytesIO()
- savefig_file = buf
- else:
- savefig_file = write_file
- buf = None
-
- # should probably add lots of other args, or use **kwargs
- if dpi:
- plt.savefig(savefig_file, dpi=dpi)
- else:
- plt.savefig(savefig_file)
- if not keep_open:
- plt.close()
- if buf:
- # need to go to the start of the buffer, if that's where it went:
- buf.seek(0)
-
- if img_format in META_IMG_FORMATS:
- use_img_tags = img_tags
- else:
- use_img_tags = None
-
- if verbose:
- postproc_st = datetime.now()
-
- if img_format in POSTPROC_IMG_FORMATS:
- image_file_postproc(write_file, img_buf=buf, img_converter=img_converter, do_trim=do_trim,
- trim_border=trim_border, logo_file=logo_file, logo_width=logo_width,
- logo_padding=logo_padding, logo_pos=logo_pos,
- do_thumb=do_thumb, img_tags=use_img_tags, verbose=verbose)
- else:
- msg = 'Currently, ImageMetaTag does not support "%s" format images' % img_format
- raise NotImplementedError(msg)
-
- # image post-processing completed, so close the buffer if we opened it:
- if buf:
- buf.close()
- if verbose:
- print 'Image post-processing took: %s' %(str(datetime.now() - postproc_st))
-
-
- # now write to the database, if it is specifed:
- if not (db_file is None or img_tags is None):
- if verbose:
- db_st = datetime.now()
-
- # if the image path can be expressed as a relative path compared
- # to the database file, then do so (unless told otherwise).
- db_dir = os.path.split(db_file)[0]
- if filename.startswith(db_dir) and not db_full_paths:
- db_filename = os.path.relpath(filename, db_dir)
- else:
- db_filename = filename
-
- wrote_db = False
- n_tries = 1
- while not wrote_db and n_tries <= db_attempts:
- try:
- db.write_img_to_dbfile(db_file, db_filename, img_tags, timeout=db_timeout)
- wrote_db = True
- except sqlite3.OperationalError as OpErr:
- if 'database is locked' in OpErr.message:
- # database being locked is what the retries and timeouts are for:
- print '%s database timeout for image "%s", writing to file "%s", %s s' \
- % (db.dt_now_str(), db_file, write_file, n_tries * db_timeout)
- n_tries += 1
- else:
- # everything else needs to be reported and raised immediately:
- msg = '{} for file {}'.format(OpErr.message, db_file)
- raise sqlite3.OperationalError(msg)
- except:
- raise
- if n_tries > db_attempts:
- raise sqlite3.OperationalError(OpErr.message)
- if verbose:
- print 'Database write took: %s' %(str(datetime.now() - db_st))
-
-[docs]def image_file_postproc(filename, outfile=None, img_buf=None, img_converter=0, do_trim=False, trim_border=0,
- logo_file=None, logo_width=40, logo_padding=0, logo_pos=0,
- do_thumb=False, img_tags=None, verbose=False):
- '''
- Does the image post-processing for :func:`ImageMetaTag.savefig`.
-
- Arguments: filename the name of the image file to process
-
- Options:
-
- * outfile - If supplied, the processing will be applied to a new file, with this name. \
- If not supplied, the post processing will overwrite the file given input file.
- * img_buf - If the image has been saved to an in-memory buffer, then supply the image buffer \
- here. This will speed up the post-processing.
- * img_converter - an integer switch controlling the level of file size compression
- * 0 - no compression
- * 1 - light compression, from RGBA to RGB
- * 2 - moderate compression, from RGBA to RGB, then to an adaptive 256 colour \
- palette.
- * 3 - heavy compression, from RGBA to RGB, then to 8-bit web standard palette.
- * do_trim - switch to trim whitespace from the edge of the image
- * trim_border - if do_trim then this can be used to define an integer number of pixels as a \
- border around the trim.
- * logo_file - a file to use as a logo, to be added to the image
- * logo_width - the desired width of the logo, in pixels. If the supplied image file is not \
- the right size, it will be resized using a method that applies filters and \
- antialiasing that works well for shrinking images with text to a much \
- smaller size. The aspect ratio of the logo image is always maintained. \
- Defaults to 40 pixels.
- * logo_padding - a number of pixels to pad around the logo (default to zero)
- * logo_pos - corner position of the logo (following pyplot.legend, but for corners):
- * 0: 'best' in this context will be upper left (default)
- * TODO: 1: 'upper right'
- * 2: 'upper left'
- * TODO: 3: 'lower left'
- * TODO: 4: 'lower right'
- * do_thumb - switch to produce default sized thumbnail, or integer/tuple to define the \
- maximum size in pixels
- * img_tags: a dictionary of tags to be added to the image metadata
- * verbose: switch for verbose output (reports file sizes before/after conversion)
- '''
-
- # usually, this is used to overwrite a file, but an outfile can be specified:
- if not outfile:
- outfile = filename
-
- if verbose:
- if img_buf:
- st_fsize = int(sys.getsizeof(img_buf))
- else:
- st_fsize = os.path.getsize(filename)
-
- if not (img_tags is None or isinstance(img_tags, dict)):
- raise ValueError('Image tags must be supplied as a dictionary')
-
- if not img_converter in range(4):
- raise ValueError('Unavailable method for image conversion')
-
- # do_thumb should equate to integer type or be a tuple of integers
- #
- # this test is taking advantage of the isinstance(do_thumb, tutple) being true before testing
- # the contents of do_thumb.
- # Also that as a bool, do_thumb also passes isinstance(do_thumb, int).
- if not isinstance(do_thumb, int) or (isinstance(do_thumb, tuple)
- and isinstance(do_thumb[0], int)
- and isinstance(do_thumb[1], int)):
- raise ValueError('Invalid thumbnail size')
-
- # do we do any image modification at all?
- modify = do_trim or do_thumb or img_tags or img_converter > 0 or logo_file is not None
-
- if img_buf:
- # if the image is in a buffer, then load it now
- im_obj = Image.open(img_buf)
- if not modify:
- # if we're not doing anyhting, then save it:
- im_obj.save(outfile, optimize=True)
- else:
- if modify:
- # use the image library to open the file:
- im_obj = Image.open(filename)
-
- if do_trim:
- # call the _im_trim routine defined above:
- im_obj = _im_trim(im_obj, border=trim_border)
-
- if logo_file is not None:
- im_obj = _im_logo(im_obj, logo_file, logo_width, logo_padding, logo_pos)
-
- if do_thumb:
- # make a thumbnail image here, if required. It is important to do this
- # before we change the colour pallette of the main image, so that there
- # are sufficent colours to do the interpolation. Afterwards, the thumbnail
- # can hage its colour table reduced as well.
- #
- # set a default thumbnail directory name and determine relative paths
- thumb_dir_name = THUMB_DEFAULT_DIR_NAME
- thumb_directory = os.path.join(os.path.split(outfile)[0], thumb_dir_name)
- thumb_full_path = os.path.join(thumb_directory, os.path.split(outfile)[1])
- # create thumbnail directory if one does not exist
- if not os.path.isdir(thumb_directory):
- os.mkdir(thumb_directory)
- # set to default thumbnail size if no size specified
- if do_thumb is True:
- do_thumb = THUMB_DEFAULT_IMG_SIZE
- # check input
- elif not isinstance(do_thumb, tuple):
- do_thumb = (do_thumb, do_thumb)
- # create the thumbnail
- im_thumb = im_obj.copy()
- im_thumb.thumbnail(do_thumb, Image.ANTIALIAS)
-
- # images start out as RGBA, strip out the alpha channel first by covnerting to RGB,
- # then you convert to the next format (that's key to keeping image quality, I think):
- if img_converter == 1:
- # this is a good quality image, but not very much smaller:
- im_obj = im_obj.convert('RGB')
- if do_thumb:
- im_thumb = im_thumb.convert('RGB')
- elif img_converter == 2:
- # second conversion to 8-bit 'P', palette mode with an adaptive palette.
- # works well for line plots.
- im_obj = im_obj.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=256)
- if do_thumb:
- im_thumb = im_thumb.convert('RGB').convert('P', palette=Image.ADAPTIVE, colors=256)
- elif img_converter == 3:
- # this is VERY strong optimisation and the result can be speckly.
- im_obj = im_obj.convert('RGB').convert('P', palette=Image.WEB)
- if do_thumb:
- im_thumb = im_thumb.convert('RGB').convert('P', palette=Image.WEB)
-
- if do_thumb:
- # now save the thumbnail:
- if img_tags:
- # add the tags
- im_thumb = _im_add_png_tags(im_thumb, img_tags)
- # and save with metadata
- _im_pngsave_addmeta(im_thumb, thumb_full_path,
- optimize=True, verbose=verbose)
- # set a thumbnail directory tag for the main image
- img_tags.update({'thumbnail directory' : thumb_dir_name})
- else:
- # simple save
- im_thumb.save(thumb_full_path, optimize=True)
-
- # now save the main image:n
- if img_tags:
- # add the tags
- im_obj = _im_add_png_tags(im_obj, img_tags)
- # and save with metadata
- _im_pngsave_addmeta(im_obj, outfile, optimize=True, verbose=verbose)
- elif modify:
- # simple save
- im_obj.save(outfile, optimize=True)
-
- if verbose:
- # now report the file size change:
- en_fsize = os.path.getsize(outfile)
- msg = 'File: "{}". Size: {}, to {} bytes ({}% original size)'
- relative_size = (100.0 * en_fsize)/st_fsize
- print msg.format(filename, st_fsize, en_fsize, relative_size)
-
-def _im_trim(im_obj, border=0):
- 'Trims an image object using Python Image Library'
- if not isinstance(border, int):
- msg = 'Input border must be an int, but is %s, %s instead' %(border, type(border))
- raise ValueError(msg)
- # make a white background:
- backg = Image.new(im_obj.mode, im_obj.size, im_obj.getpixel((0, 0)))
- # do an image difference:
- diff = ImageChops.difference(im_obj, backg)
- # add it together
- diff = ImageChops.add(diff, diff, 1.0, -100)
- # and see what the bbox is of that...
- bbox = diff.getbbox()
-
- if border != 0:
- border_bbox = [-border, -border, border, border]
- # now apply that trim:
- bbox_tr = [x+y for x, y in zip(bbox, border_bbox)]
-
- # bbox defines the first corner as top+left, then the second corner as bottom+right
- # (not the bottom left corner, and the width, height from there)
- if bbox_tr[0] < 0:
- bbox_tr[0] = 0
- if bbox_tr[1] < 0:
- bbox_tr[1] = 0
- if bbox_tr[2] > im_obj.size[0]:
- bbox_tr[2] = im_obj.size[0]
- if bbox_tr[3] > im_obj.size[1]:
- bbox_tr[3] = im_obj.size[1]
- # now check to see if that's actually foing to do anything:
- if bbox_tr == [0, 0, im_obj.size[0], im_obj.size[1]]:
- bbox = None
- else:
- bbox = bbox_tr
-
- if bbox:
- # crop:
- return im_obj.crop(bbox)
- else:
- return im_obj
-
-def _im_logo(im_obj, logo_file, logo_width, logo_padding, logo_pos):
- 'adds a logo to the required corner of an image object (usually after an im_trim)'
-
- # load in the logo file image:
- logo_obj = Image.open(logo_file)
- # rescale to the new width and height:
- if logo_width != logo_obj.size[0]:
- logo_height = int(logo_obj.size[1] * float(logo_width) / logo_obj.size[0])
- res_logo_obj = _img_stong_resize(logo_obj, size=(logo_width, logo_height))
- else:
- res_logo_obj = logo_obj
-
- # TODO: this is written for putting a logo in the top-left corner, but could be extended:
- if logo_pos in [0, 2]:
-
- # now pull out a sub image from the main image, that's just where the logo would go,
- # it it were this is the size we want to have blank, to put the logo, including padding:
- req_logo_size = [x + 2*logo_padding for x in res_logo_obj.size]
-
- corner_obj = im_obj.crop((0, 0, req_logo_size[0], req_logo_size[1]))
- #
- # now get a bounding box as though we were trimming this image:
- backg = Image.new(corner_obj.mode, corner_obj.size, corner_obj.getpixel((0, 0)))
- # do an image difference:
- diff = ImageChops.difference(corner_obj, backg)
- # add it together
- diff = ImageChops.add(diff, diff, 1.0, -100)
- # and see what the bbox is of that...
- bbox = diff.getbbox()
-
-# # get the offset in x and y:
-# if bbox is None:
-# # the corner object is empty so no need to offset:
-# offsets = (0,0)
-# else:
-# offsets = (req_logo_size[0] - bbox[0], req_logo_size[1] - bbox[1])
-# # but you only ever need to offset in one direction (the shortest one):
-# offset = min(offsets)
-# offset_ind = offsets.index(offset)
-
- # as this is the top left corner of a plot, the logo should be offset only
- # in x (so the title is still at the top)
- if bbox is None:
- # the corner object is empty so no need to offset:
- offset = 0
- else:
- offset = req_logo_size[0] - bbox[0]
- offset_ind = 0
-
-
- # now put that together to make an image:
-
- # create the blank image:
- new_size = list(im_obj.size)
- new_size[offset_ind] += offset
- new_obj = Image.new(im_obj.mode, new_size, im_obj.getpixel((0, 0)))
-
- # now put the main image into it, offset:
- if offset_ind == 0:
- offsets = (offset, 0)
- else:
- offsets = (0, offset)
- new_obj.paste(im_obj, offsets)
- # and put the rescaled logo onto it too, in the right place:
- new_obj.paste(res_logo_obj, (logo_padding, logo_padding))
-
- else:
- msg = 'logo positions other than 0 and 2 (both top left) have not been implemented yet'
- raise NotImplementedError(msg)
-
- return new_obj
-
-def _im_add_png_tags(im_obj, png_tags):
- 'adds img_tags to an image object for later saving'
- for key, val in png_tags.iteritems():
- im_obj.info[key] = val
- return im_obj
-
-def _im_pngsave_addmeta(im_obj, outfile, optimize=True, verbose=False):
- 'saves an image object to a png file, adding metadata using the info tag...'
- # these can be automatically added to Image.info dict
- # they are not user-added metadata
- reserved = ('interlace', 'gamma', 'dpi', 'transparency', 'aspect', 'signature', \
- 'date:create', 'date:modify')
-
- # undocumented class
- meta = PngImagePlugin.PngInfo()
-
- # copy metadata into new object
- for key, val in im_obj.info.iteritems():
- if key in reserved:
- pass
- elif val is None:
- if verbose:
- print 'key "%s" is set to None' % key
- else:
- meta.add_text(key, val, 0)
-
- # and save
- im_obj.save(outfile, "PNG", optimize=optimize, pnginfo=meta)
-
-def _img_stong_resize(img_obj, size=None):
- 'does image pre-processing before a strong resize, to get rid of halo effects'
- if size is None:
- size = (40, 40)
- #shrink_ratio = [x/float(y) for x,y in zip(img_obj.size, size)]
- # make sure the image has an alpha channel:
- img_obj = img_obj.convert('RGBA')
- # premultiply the alpha channel:
- new_img_obj = _img_premultiplyAlpha(img_obj)
- ## and smooth is the change is size is large:
- #if max(shrink_ratio) >= 2:
- # new_img_obj = new_img_obj.filter(ImageFilter.SMOOTH)
- # now resize:
- res_img_obj = new_img_obj.resize(size, Image.ANTIALIAS)
- return res_img_obj
-
-def _img_premultiplyAlpha(img_obj):
- 'Premultiplies an input image by its alpha channel, which is useful for stron resizes'
- # fake transparent image to blend with
- transparent = Image.new("RGBA", img_obj.size, (0, 0, 0, 0))
- # blend with transparent image using own alpha
- return Image.composite(img_obj, transparent, img_obj)
-
-
-
-
-
-
-
-
- Navigation
-
- -
- index
- -
- modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
-
-
-
- © Copyright 2015-2017, British Crown Copyright.
- Created using Sphinx 1.4.8.
-
-
-
\ No newline at end of file
diff --git a/docs/build/html/_modules/ImageMetaTag/webpage.html b/docs/build/html/_modules/ImageMetaTag/webpage.html
index a6d77e1..76e4ba2 100644
--- a/docs/build/html/_modules/ImageMetaTag/webpage.html
+++ b/docs/build/html/_modules/ImageMetaTag/webpage.html
@@ -6,7 +6,7 @@
- ImageMetaTag.webpage — ImageMetaTag 0.6.4 documentation
+ ImageMetaTag.webpage — ImageMetaTag 0.6.5 documentation
@@ -14,7 +14,7 @@
-
-
-
-
+
+
-
-
+
+
Navigation
-
@@ -38,23 +36,27 @@
Navigation
-
modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
+ - ImageMetaTag 0.6.5 documentation »
+ - Module code »
+ - ImageMetaTag »
-
+
-
+
Quick search
+
+ Enter search terms or a module, class or function name.
+
@@ -63,10 +65,10 @@ Quick search
-
+
Source code for ImageMetaTag.webpage
-'''
+'''
This sub-module contains functions to write out an :class:`ImageMetaTag.ImageDict` to a webpage.
The webpages are made up of a single .html file, which is the page to be loaded to view the images.
@@ -105,28 +107,28 @@ Source code for ImageMetaTag.webpage
'''
import os, json, pdb, shutil, tempfile, zlib
-import numpy as np
-import ImageMetaTag as imt
+import numpy as np
+import ImageMetaTag as imt
-from multiprocessing import Pool
+from multiprocessing import Pool
-# single indent to be used on the output webpage
-INDENT = ' '
+# single indent to be used on the output webpage
+INDENT = ' '
LEN_INDENT = len(INDENT)
-# for compressed json files, we use pako to inflate the data back to full size:
-PAKO_JS_FILE = 'pako_inflate.js'
-PAKO_RELEASE = '1.0.5'
-PAKO_SOURE_TAR = 'https://github.com/nodeca/pako/archive/{}.tar.gz'.format(PAKO_RELEASE)
+# for compressed json files, we use pako to inflate the data back to full size:
+PAKO_JS_FILE = 'pako_inflate.js'
+PAKO_RELEASE = '1.0.5'
+PAKO_SOURE_TAR = 'https://github.com/nodeca/pako/archive/{}.tar.gz'.format(PAKO_RELEASE)
-[docs]def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=None,
- preamble=None, postamble=None, compression=False,
- initial_selectors=None, show_selector_names=False,
- url_type='int', only_show_rel_url=False, verbose=False,
- style='horiz dropdowns', write_intmed_tmpfile=False,
- description=None, keywords=None):
+[docs]def write_full_page(img_dict, filepath, title, page_filename=None, tab_s_name=None,
+ preamble=None, postamble=None, compression=False,
+ initial_selectors=None, show_selector_names=False,
+ url_type='int', only_show_rel_url=False, verbose=False,
+ style='horiz dropdowns', write_intmed_tmpfile=False,
+ description=None, keywords=None):
'''
Writes out an :class:`ImageMetaTag.ImageDict` as a webpage, to a given file location.
The file is overwritten.
@@ -166,143 +168,143 @@ Source code for ImageMetaTag.webpage
page_dependencies = []
- if not (isinstance(img_dict, imt.ImageDict) or img_dict is None):
- raise ValueError('write_full_page works on an ImageMetaTag ImageDict.')
+ if not (isinstance(img_dict, imt.ImageDict) or img_dict is None):
+ raise ValueError('write_full_page works on an ImageMetaTag ImageDict.')
- if page_filename is None:
+ if page_filename is None:
page_filename = os.path.basename(filepath)
- # other files involved:
+ # other files involved:
file_dir, file_name = os.path.split(filepath)
page_dependencies.append(file_name)
- if img_dict is None:
+ if img_dict is None:
json_files = []
else:
- # now make sure the required javascript library is copied over to the file_dir:
+ # now make sure the required javascript library is copied over to the file_dir:
js_files = copy_required_javascript(file_dir, style, compression=compression)
page_dependencies.extend(js_files)
- # we have real data to work with:
- # this tests the dict has uniform_depth, which is needed for all current webpages.
- dict_depth = img_dict.dict_depth(uniform_depth=True)
- # work out what files we need to create:
+ # we have real data to work with:
+ # this tests the dict has uniform_depth, which is needed for all current webpages.
+ dict_depth = img_dict.dict_depth(uniform_depth=True)
+ # work out what files we need to create:
file_name_no_ext = os.path.splitext(file_name)[0]
- # json file to hold the image_dict branching data etc:
+ # json file to hold the image_dict branching data etc:
json_file_no_ext = os.path.join(file_dir, file_name_no_ext)
json_files = write_json(img_dict, json_file_no_ext, compression=compression)
- # the final page is dependent on the final locations of the json files,
- # relative to the html:
+ # the final page is dependent on the final locations of the json files,
+ # relative to the html:
page_dependencies.extend([os.path.split(x[1])[1] for x in json_files])
- # this is the internal name the different selectors, associated lists for the selectors, and
- # the list of files (all with a numbered suffix):
- selector_prefix = 'sel'
- url_separator = '|'
+ # this is the internal name the different selectors, associated lists for the selectors, and
+ # the list of files (all with a numbered suffix):
+ selector_prefix = 'sel'
+ url_separator = '|'
- # now write the actual output file:
+ # now write the actual output file:
if write_intmed_tmpfile:
- # get a temporary file:
- with tempfile.NamedTemporaryFile('w', suffix='.html', prefix='imt_',
- dir=file_dir, delete=False) as html_file_obj:
+ # get a temporary file:
+ with tempfile.NamedTemporaryFile('w', suffix='.html', prefix='imt_',
+ dir=file_dir, delete=False) as html_file_obj:
tmp_html_filepath = html_file_obj.name
filepath_to_write = tmp_html_filepath
else:
filepath_to_write = filepath
- # start the indent:
- ind = ''
-
- # open the file - this is a nice and simple file so just use the with open...
- with open(filepath_to_write, 'w') as out_file:
- # write out the start of the file:
- out_file.write('<!DOCTYPE html>\n')
- out_file.write(ind + '<html>\n')
- # increase the indent level:
+ # start the indent:
+ ind = ''
+
+ # open the file - this is a nice and simple file so just use the with open...
+ with open(filepath_to_write, 'w') as out_file:
+ # write out the start of the file:
+ out_file.write('<!DOCTYPE html>\n')
+ out_file.write(ind + '<html>\n')
+ # increase the indent level:
ind = _indent_up_one(ind)
- out_file.write(ind + '<head>\n')
+ out_file.write(ind + '<head>\n')
ind = _indent_up_one(ind)
- if not title is None:
- out_file.write('{}<title>{}</title>\n'.format(ind, title))
- out_str = ind+'<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n'
+ if not title is None:
+ out_file.write('{}<title>{}</title>\n'.format(ind, title))
+ out_str = ind+'<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">\n'
out_file.write(out_str)
- if style == 'horiz dropdowns':
- # write out a little css at the top:
- css = '''{0}<style>
-{0} body, div, dl, dt, dd, li, h1, h2 {{
-{0} margin: 0;
-{0} padding: 0;
-{0} }}
-{0} h3, h4, h5, h6, pre, form, fieldset, input {{'
-{0} margin: 0;
-{0} padding: 0;
-{0} }}
-{0} textarea, p, blockquote, th, td {{
-{0} margin: 0;
-{0} padding: 0;
-{0} }}
-{0} fieldset, img {{
-{0} border: 0 none;
-{0} }}
-{0} body {{
-{0} font: 12px Myriad,Helvetica,Tahoma,Arial,clean,sans-serif;
-{0} *font-size: 75%;
-{0} }}
-{0} h1 {{
-{0} font-size: 1.5em;
-{0} font-weight: normal;
-{0} line-height: 1em;
-{0} margin-top: 1em;
-{0} margin-bottom:0;
-{0} }}
-{0} h2 {{
-{0} font-size: 1.1667em;
-{0} font-weight: bold;
-{0} line-height: 1.286em;
-{0} margin-top: 1.929em;
-{0} margin-bottom:0.643em;
-{0} }}
-{0} h3, h4, h5, h6 {{
-{0} font-size: 1em;
-{0} font-weight: bold;
-{0} line-height: 1.5em;
-{0} margin-top: 1.5em;
-{0} margin-bottom: 0;
-{0} }}
-{0} p {{
-{0} font-size: 1em;
-{0} margin-top: 1.5em;
-{0} margin-bottom: 1.5em;
-{0} line-height: 1.5em;
-{0} }}
-{0} pre, code {{
-{0} font-size:115%;
-{0} *font-size:100%;
-{0} font-family: Courier, "Courier New";
-{0} background-color: #efefef;
-{0} border: 1px solid #ccc;
-{0} }}
-{0} pre {{
-{0} border-width: 1px 0;
-{0} padding: 1.5em;
-{0} }}
-{0} table {{
-{0} font-size:100%;
-{0} }}
-{0}</style>
-'''
+ if style == 'horiz dropdowns':
+ # write out a little css at the top:
+ css = '''{0}<style>
+{0} body, div, dl, dt, dd, li, h1, h2 {{
+{0} margin: 0;
+{0} padding: 0;
+{0} }}
+{0} h3, h4, h5, h6, pre, form, fieldset, input {{'
+{0} margin: 0;
+{0} padding: 0;
+{0} }}
+{0} textarea, p, blockquote, th, td {{
+{0} margin: 0;
+{0} padding: 0;
+{0} }}
+{0} fieldset, img {{
+{0} border: 0 none;
+{0} }}
+{0} body {{
+{0} font: 12px Myriad,Helvetica,Tahoma,Arial,clean,sans-serif;
+{0} *font-size: 75%;
+{0} }}
+{0} h1 {{
+{0} font-size: 1.5em;
+{0} font-weight: normal;
+{0} line-height: 1em;
+{0} margin-top: 1em;
+{0} margin-bottom:0;
+{0} }}
+{0} h2 {{
+{0} font-size: 1.1667em;
+{0} font-weight: bold;
+{0} line-height: 1.286em;
+{0} margin-top: 1.929em;
+{0} margin-bottom:0.643em;
+{0} }}
+{0} h3, h4, h5, h6 {{
+{0} font-size: 1em;
+{0} font-weight: bold;
+{0} line-height: 1.5em;
+{0} margin-top: 1.5em;
+{0} margin-bottom: 0;
+{0} }}
+{0} p {{
+{0} font-size: 1em;
+{0} margin-top: 1.5em;
+{0} margin-bottom: 1.5em;
+{0} line-height: 1.5em;
+{0} }}
+{0} pre, code {{
+{0} font-size:115%;
+{0} *font-size:100%;
+{0} font-family: Courier, "Courier New";
+{0} background-color: #efefef;
+{0} border: 1px solid #ccc;
+{0} }}
+{0} pre {{
+{0} border-width: 1px 0;
+{0} padding: 1.5em;
+{0} }}
+{0} table {{
+{0} font-size:100%;
+{0} }}
+{0}</style>
+'''
out_file.write(css.format(ind))
- # now write out the specific stuff to the html header:
- if img_dict is None:
- # an empty img_dict needs very little:
+ # now write out the specific stuff to the html header:
+ if img_dict is None:
+ # an empty img_dict needs very little:
write_js_to_header(img_dict,
file_obj=out_file,
pagename=page_filename, tabname=tab_s_name,
ind=ind,
description=description, keywords=keywords)
else:
- # the json_files is a list of (tmp_file, final_file) tuples. Here we want the final one:
+ # the json_files is a list of (tmp_file, final_file) tuples. Here we want the final one:
final_json_files = [os.path.split(x[1])[1] for x in json_files]
write_js_to_header(img_dict, initial_selectors=initial_selectors,
file_obj=out_file, json_files=final_json_files, js_files=js_files,
@@ -311,45 +313,45 @@ Source code for ImageMetaTag.webpage
url_type=url_type, only_show_rel_url=only_show_rel_url,
style=style, ind=ind, compression=compression,
description=description, keywords=keywords)
- # now close the script and head:
+ # now close the script and head:
ind = _indent_down_one(ind)
- out_file.write(ind + '</script>\n')
+ out_file.write(ind + '</script>\n')
ind = _indent_down_one(ind)
- out_file.write(ind + '</head>\n')
+ out_file.write(ind + '</head>\n')
- # now start the body:
- margins = 'leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"'
- bgcolor = 'bcolor="#FFFFFF"'
- text_color = 'text="#000000"'
- out_file.write('{}<body {} {} {}>\n'.format(ind, bgcolor, text_color, margins))
+ # now start the body:
+ margins = 'leftmargin="0" topmargin="0" marginwidth="0" marginheight="0"'
+ bgcolor = 'bcolor="#FFFFFF"'
+ text_color = 'text="#000000"'
+ out_file.write('{}<body {} {} {}>\n'.format(ind, bgcolor, text_color, margins))
- # the preamble is the first thing to go in the body:
- if not preamble is None:
+ # the preamble is the first thing to go in the body:
+ if not preamble is None:
out_file.write(preamble)
- # now the img_dict content:
- if img_dict is None:
- out_file.write('<p><h1>No images are available for this page.</h1></p>')
+ # now the img_dict content:
+ if img_dict is None:
+ out_file.write('<p><h1>No images are available for this page.</h1></p>')
else:
- # now write out the end, which includes the placeholders for the actual
- # stuff that appears on the page:
+ # now write out the end, which includes the placeholders for the actual
+ # stuff that appears on the page:
if show_selector_names:
level_names = img_dict.level_names
else:
- level_names = False
- # if we're labelling selectors, and we have an animator button, label that too:
+ level_names = False
+ # if we're labelling selectors, and we have an animator button, label that too:
if img_dict.selector_animated > 1 and show_selector_names:
anim_level = level_names[img_dict.selector_animated]
else:
- anim_level = None
+ anim_level = None
write_js_placeholders(file_obj=out_file, dict_depth=img_dict.dict_depth(),
style=style, level_names=level_names,
animated_level=anim_level)
- # the body is done, so the postamble comes in:
- if not postamble is None:
- out_file.write(postamble + '\n')
- # finish the body, and html:
- out_file.write(ind + '</body>\n')
- out_file.write('\n</html>')
+ # the body is done, so the postamble comes in:
+ if not postamble is None:
+ out_file.write(postamble + '\n')
+ # finish the body, and html:
+ out_file.write(ind + '</body>\n')
+ out_file.write('\n</html>')
if write_intmed_tmpfile:
@@ -357,21 +359,21 @@ Source code for ImageMetaTag.webpage
else:
tmp_files_to_mv = json_files
for tmp_file_mv in tmp_files_to_mv:
- # now move the json, then the html files:
- os.chmod(tmp_file_mv[0], 0644)
+ # now move the json, then the html files:
+ os.chmod(tmp_file_mv[0], 0644)
shutil.move(tmp_file_mv[0], tmp_file_mv[1])
if verbose:
- print 'File "%s" complete.' % filepath
-
- return page_dependencies
+ print 'File "%s" complete.' % filepath
-[docs]def write_js_to_header(img_dict, initial_selectors=None, style=None,
- file_obj=None, json_files=None, js_files=None,
- pagename=None, tabname=None, selector_prefix=None,
- url_separator='|', url_type='str', only_show_rel_url=False,
- ind=None, compression=False,
- description=None, keywords=None):
+ return page_dependencies
+
+[docs]def write_js_to_header(img_dict, initial_selectors=None, style=None,
+ file_obj=None, json_files=None, js_files=None,
+ pagename=None, tabname=None, selector_prefix=None,
+ url_separator='|', url_type='str', only_show_rel_url=False,
+ ind=None, compression=False,
+ description=None, keywords=None):
'''
Writes out the required ImageMetaTag config and data paths into a html header section
for an input :class:`ImageMetaTag.ImageDict`.
@@ -397,144 +399,144 @@ Source code for ImageMetaTag.webpage
* description - html description metadata7
* keywords - html keyword metadata
'''
- if not (isinstance(img_dict, imt.ImageDict) or img_dict is None):
- raise ValueError('Input img_dict is not an ImageMetaTag ImageDict')
+ if not (isinstance(img_dict, imt.ImageDict) or img_dict is None):
+ raise ValueError('Input img_dict is not an ImageMetaTag ImageDict')
- if not description is None:
- file_obj.write('{}<meta name="description" content="{}">\n'.format(ind, description))
- if not keywords is None:
- file_obj.write('{}<meta name="keywords" content="{}">\n'.format(ind, keywords))
+ if not description is None:
+ file_obj.write('{}<meta name="description" content="{}">\n'.format(ind, description))
+ if not keywords is None:
+ file_obj.write('{}<meta name="keywords" content="{}">\n'.format(ind, keywords))
- if not img_dict is None:
- ## add a reference to the data structure:
- #out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, json_files)
- #file_obj.write(out_str)
+ if not img_dict is None:
+ ## add a reference to the data structure:
+ #out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, json_files)
+ #file_obj.write(out_str)
- # now add a reference to the javascript functions to implement the style:
+ # now add a reference to the javascript functions to implement the style:
for js_file in js_files:
- out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, js_file)
+ out_str = '{}<script type="text/javascript" src="{}"></script>\n'.format(ind, js_file)
file_obj.write(out_str)
- # now write out the javascript cnfiguration variables:
- file_obj.write(ind + '<script type="text/javascript">\n')
+ # now write out the javascript cnfiguration variables:
+ file_obj.write(ind + '<script type="text/javascript">\n')
ind = _indent_up_one(ind)
- # define, read in and parse the json file:
- out_str = '''{0}var json_files = {1};
-{0}var zl_unpack = {2};
-{0}imt = read_parse_json_files(json_files, zl_unpack);
-'''
+ # define, read in and parse the json file:
+ out_str = '''{0}var json_files = {1};
+{0}var zl_unpack = {2};
+{0}imt = read_parse_json_files(json_files, zl_unpack);
+'''
file_obj.write(out_str.format(ind, json_files, _py_to_js_bool(bool(compression))))
- # in case the page we are writing is embedded as a frame, write out the top
- # level page here;
- file_obj.write('{}var pagename = "{}"\n'.format(ind, pagename))
- # the tab name is used in setting up the URL in nested frames:
- file_obj.write('{}var tab_name = "{}";\n'.format(ind, tabname))
+ # in case the page we are writing is embedded as a frame, write out the top
+ # level page here;
+ file_obj.write('{}var pagename = "{}"\n'.format(ind, pagename))
+ # the tab name is used in setting up the URL in nested frames:
+ file_obj.write('{}var tab_name = "{}";\n'.format(ind, tabname))
dict_depth = img_dict.dict_depth()
- # the key_to_selector variable is what maps each set of keys onto a selector on the page:
+ # the key_to_selector variable is what maps each set of keys onto a selector on the page:
key_to_selector = str([selector_prefix + str(x) for x in range(dict_depth)])
- file_obj.write('{}var key_to_selector = {};\n'.format(ind, key_to_selector))
- # this determines whether a selector uses the animation controls on a page:
- file_obj.write('{}var anim_sel = {};\n'.format(ind, img_dict.selector_animated))
- # and the direction the animation runs in:
- file_obj.write('{}var anim_dir = {};\n'.format(ind, img_dict.animation_direction))
- # and the direction the animation runs in:
-
- # the url_separator is the text character that goes between the variables in the url:
- if url_separator == '&':
- msg = 'Cannot use "&" as the url_separator, as some strings will '
- msg += 'become html special characters. For instance ¶-global '
- msg += 'will be treated as a paragraph then -global, not the intended string.'
+ file_obj.write('{}var key_to_selector = {};\n'.format(ind, key_to_selector))
+ # this determines whether a selector uses the animation controls on a page:
+ file_obj.write('{}var anim_sel = {};\n'.format(ind, img_dict.selector_animated))
+ # and the direction the animation runs in:
+ file_obj.write('{}var anim_dir = {};\n'.format(ind, img_dict.animation_direction))
+ # and the direction the animation runs in:
+
+ # the url_separator is the text character that goes between the variables in the url:
+ if url_separator == '&':
+ msg = 'Cannot use "&" as the url_separator, as some strings will '
+ msg += 'become html special characters. For instance ¶-global '
+ msg += 'will be treated as a paragraph then -global, not the intended string.'
raise ValueError(msg)
- file_obj.write('{}var url_separator = "{}";\n'.format(ind, url_separator))
-
- # the url_type determines whether the url is full of integers (int), with meaningful
- # values internally or text which looks more meaningful to the user:
- file_obj.write('{}var url_type = "{}";\n'.format(ind, url_type))
- # the show_rel_url logical (converted to a string to init a javascript bool)
- file_obj.write('{}var show_rel_url = {};\n'.format(ind, _py_to_js_bool(only_show_rel_url)))
-
- # the selected_id needs to be defined here too, as it's used as a global variable
- # (it will be overwritten later if the URL changes it, and when selectors change it):
- if initial_selectors is None:
- # if it's not set, then set it to something invalid, and the validator
- # in the javascript will sort it out. It MUST be the right length though:
- file_obj.write('{}var selected_id = {}\n;'.format(ind, str([-1]*dict_depth)))
+ file_obj.write('{}var url_separator = "{}";\n'.format(ind, url_separator))
+
+ # the url_type determines whether the url is full of integers (int), with meaningful
+ # values internally or text which looks more meaningful to the user:
+ file_obj.write('{}var url_type = "{}";\n'.format(ind, url_type))
+ # the show_rel_url logical (converted to a string to init a javascript bool)
+ file_obj.write('{}var show_rel_url = {};\n'.format(ind, _py_to_js_bool(only_show_rel_url)))
+
+ # the selected_id needs to be defined here too, as it's used as a global variable
+ # (it will be overwritten later if the URL changes it, and when selectors change it):
+ if initial_selectors is None:
+ # if it's not set, then set it to something invalid, and the validator
+ # in the javascript will sort it out. It MUST be the right length though:
+ file_obj.write('{}var selected_id = {}\n;'.format(ind, str([-1]*dict_depth)))
else:
if not isinstance(initial_selectors, list):
- msg = 'Input initial_selectors must be a list, of length the depth of the ImageDict'
+ msg = 'Input initial_selectors must be a list, of length the depth of the ImageDict'
raise ValueError(msg)
if len(initial_selectors) != img_dict.dict_depth():
- msg = 'Input initial_selectors must be a list, of length the depth of the ImageDict'
+ msg = 'Input initial_selectors must be a list, of length the depth of the ImageDict'
raise ValueError(msg)
- # the input can either be a list of integer indices, or strings that match:
+ # the input can either be a list of integer indices, or strings that match:
initial_selectors_as_inds = []
initial_selectors_as_string = []
for i_sel, sel_value in enumerate(initial_selectors):
if isinstance(sel_value, int):
if sel_value < 0 or sel_value >= len(img_dict.keys[i_sel]):
- raise ValueError('initial_selectors are out of range')
- # store the initial_selectors_as_inds
+ raise ValueError('initial_selectors are out of range')
+ # store the initial_selectors_as_inds
initial_selectors_as_inds.append(sel_value)
- # and as a string:
+ # and as a string:
initial_selectors_as_string.append(img_dict.keys[i_sel][sel_value])
else:
- # get the index of that value:
+ # get the index of that value:
initial_selectors_as_inds.append(img_dict.keys[i_sel].index(sel_value))
- # and simple store the string:
+ # and simple store the string:
initial_selectors_as_string.append(sel_value)
- # check that's valid:
- if img_dict.return_from_list(initial_selectors_as_string) is None:
- raise ValueError('Input initial_selectors does not end up at a valid image/payload')
- # write that out:
- file_obj.write('{}var selected_id = {};\n'.format(ind, initial_selectors_as_inds))
+ # check that's valid:
+ if img_dict.return_from_list(initial_selectors_as_string) is None:
+ raise ValueError('Input initial_selectors does not end up at a valid image/payload')
+ # write that out:
+ file_obj.write('{}var selected_id = {};\n'.format(ind, initial_selectors_as_inds))
- # now write out the lists of keys, to the different levels:
+ # now write out the lists of keys, to the different levels:
keys_to_js = [str(x[1]) for x in img_dict.keys.iteritems()]
- file_obj.write('{}var key_lists = [{},\n'.format(ind, keys_to_js[0]))
+ file_obj.write('{}var key_lists = [{},\n'.format(ind, keys_to_js[0]))
ind = _indent_up_one(ind)
for i_depth in range(1, dict_depth):
- file_obj.write('{}{},\n'.format(ind, keys_to_js[i_depth]))
+ file_obj.write('{}{},\n'.format(ind, keys_to_js[i_depth]))
ind = _indent_down_one(ind)
- file_obj.write(ind + '];\n')
-
- # now some top level things:
- if style == 'horiz dropdowns':
- file_obj.write('''
- {0}// other top level derived variables
- {0}// the depth of the ImageMetaTag ImageDict (number of selectors):
- {0}var n_deep = selected_id.length;
- {0}// a list of the options available to the animator buttons, with the current selectio
- {0}var anim_options = [];
- {0}// the index of the current option for the animator:
- {0}var anim_ind = 0;
- '''.format(ind))
-
- # now, the main call:
- file_obj.write(ind + 'window.onload = function() {\n')
+ file_obj.write(ind + '];\n')
+
+ # now some top level things:
+ if style == 'horiz dropdowns':
+ file_obj.write('''
+ {0}// other top level derived variables
+ {0}// the depth of the ImageMetaTag ImageDict (number of selectors):
+ {0}var n_deep = selected_id.length;
+ {0}// a list of the options available to the animator buttons, with the current selectio
+ {0}var anim_options = [];
+ {0}// the index of the current option for the animator:
+ {0}var anim_ind = 0;
+ '''.format(ind))
+
+ # now, the main call:
+ file_obj.write(ind + 'window.onload = function() {\n')
ind = _indent_up_one(ind)
- file_obj.write(ind + '// redefine onload, so it does this:\n')
- file_obj.write(ind + 'imt_main();\n')
+ file_obj.write(ind + '// redefine onload, so it does this:\n')
+ file_obj.write(ind + 'imt_main();\n')
ind = _indent_down_one(ind)
- file_obj.write(ind + '};\n')
- # END of the imt specifc header content:
-
-def write_js_setup_defaults(selector_prefix=None, list_prefix=None, file_list_name=None):
+ file_obj.write(ind + '};\n')
+ # END of the imt specifc header content:
+
+def write_js_setup_defaults(selector_prefix=None, list_prefix=None, file_list_name=None):
'''
this specifies defaults for the internal names the different selectors, associated lists for
the selectors, and the list of files (all with a numbered suffix)
'''
- if selector_prefix is None:
- selector_prefix = 'sel'
- if list_prefix is None:
- list_prefix = 'list'
- if file_list_name is None:
- file_list_name = 'file_list'
+ if selector_prefix is None:
+ selector_prefix = 'sel'
+ if list_prefix is None:
+ list_prefix = 'list'
+ if file_list_name is None:
+ file_list_name = 'file_list'
return (selector_prefix, list_prefix, file_list_name)
-[docs]def write_json(img_dict, file_name_no_ext, compression=False,
- chunk_char_limit=1e7):
+[docs]def write_json(img_dict, file_name_no_ext, compression=False,
+ chunk_char_limit=1e7):
'''
Writes a json dump of the :class:`ImageMetaTag.ImageDict` tree strucuture
to a target file path.
@@ -548,53 +550,53 @@ Source code for ImageMetaTag.webpage
'''
def json_from_dict(in_dict):
- 'returns a json string from an input dict'
- return json.dumps(in_dict, separators=(',', ':'))
+ 'returns a json string from an input dict'
+ return json.dumps(in_dict, separators=(',', ':'))
if isinstance(img_dict, imt.ImageDict):
dict_as_json = json_from_dict(img_dict.dict)
elif isinstance(img_dict, str):
dict_as_json = img_dict
else:
- raise ValueError('input img_dict is not an ImageMetaTag.ImageDict or string')
+ raise ValueError('input img_dict is not an ImageMetaTag.ImageDict or string')
- # file suffix:
- suffix = '.json'
+ # file suffix:
+ suffix = '.json'
if compression:
- suffix += '.zlib'
+ suffix += '.zlib'
- # the output files:
+ # the output files:
out_files = []
tmp_file_dir = os.path.split(file_name_no_ext)[0]
- # use the maximum length of a single string per file:
+ # use the maximum length of a single string per file:
n_chunks = np.ceil(len(dict_as_json) / chunk_char_limit)
n_chunks = int(n_chunks)
if n_chunks == 1:
- # easy if it fits into a single file:
+ # easy if it fits into a single file:
json_file = file_name_no_ext + suffix
- with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
- dir=tmp_file_dir, delete=False) as file_obj:
+ with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
+ dir=tmp_file_dir, delete=False) as file_obj:
if compression:
file_obj.write(zlib.compress(dict_as_json))
else:
file_obj.write(dict_as_json)
tmp_file_path = file_obj.name
- # make a note of the outputs:
+ # make a note of the outputs:
out_files.append((tmp_file_path, json_file))
else:
- # find the appropriate depth at which to split the dict:
+ # find the appropriate depth at which to split the dict:
if not isinstance(img_dict, imt.ImageDict):
- msg = 'Large data sets need to be supplied as an ImageDict, so they can be split'
+ msg = 'Large data sets need to be supplied as an ImageDict, so they can be split'
raise ValueError(msg)
- dict_depth = img_dict.dict_depth(uniform_depth=True)
+ dict_depth = img_dict.dict_depth(uniform_depth=True)
if len(img_dict.keys) != dict_depth:
- raise ValueError('Inconsistent depth and keys. Do the keys need relisting?')
+ raise ValueError('Inconsistent depth and keys. Do the keys need relisting?')
- # determine approximately how many splits will be obtained by breaking up the
- # dictionary at each level, assuming the tree structure branches uniformly.
+ # determine approximately how many splits will be obtained by breaking up the
+ # dictionary at each level, assuming the tree structure branches uniformly.
n_by_depth = []
for i_depth in range(dict_depth):
if i_depth == 0:
@@ -603,68 +605,68 @@ Source code for ImageMetaTag.webpage
n_by_depth.append(len(img_dict.keys[i_depth]) * n_by_depth[-1])
if n_by_depth[-1] >= n_chunks:
break
- # i_depth is an index, but depth is the number of levels, so needs one adding:
+ # i_depth is an index, but depth is the number of levels, so needs one adding:
depth = i_depth + 1
- # get all the combinations of keys that reach the required depth:
+ # get all the combinations of keys that reach the required depth:
keys, array_inds = img_dict.dict_index_array(maxdepth=depth)
- # now loop through the array_inds. Each one contains the indices of a valid path through
- # the dict, to the required depth. Each subdict will be written to a separate .json file
+ # now loop through the array_inds. Each one contains the indices of a valid path through
+ # the dict, to the required depth. Each subdict will be written to a separate .json file
paths = []
top_dict = {}
for i_json, path_inds in enumerate(array_inds):
- # traverse to the subdict, given by the current path,
- # storing the keys of the path along the way
+ # traverse to the subdict, given by the current path,
+ # storing the keys of the path along the way
subdict = img_dict.dict
path = []
for level, ind in enumerate(path_inds):
subdict = subdict[keys[level][ind]]
path.append(keys[level][ind])
- # convert the subdict to .json
+ # convert the subdict to .json
subdict_as_json = json_from_dict(subdict)
- # and write this out:
- json_file = '{}_{}{}'.format(file_name_no_ext, i_json, suffix)
- with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
- dir=tmp_file_dir, delete=False) as file_obj:
+ # and write this out:
+ json_file = '{}_{}{}'.format(file_name_no_ext, i_json, suffix)
+ with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
+ dir=tmp_file_dir, delete=False) as file_obj:
if compression:
file_obj.write(zlib.compress(subdict_as_json))
else:
file_obj.write(subdict_as_json)
tmp_file_path = file_obj.name
- # make a note of the outputs:
+ # make a note of the outputs:
out_files.append((tmp_file_path, json_file))
- # now add this to the top level dict structure, so the final json file can pull them all togther:
- path_dict = {path[-1]: '**FILE_{}**'.format(i_json)}
- # go backwards, from the second from last element of the path, to add more;
+ # now add this to the top level dict structure, so the final json file can pull them all togther:
+ path_dict = {path[-1]: '**FILE_{}**'.format(i_json)}
+ # go backwards, from the second from last element of the path, to add more;
for key in path[-2::-1]:
path_dict = {key: path_dict}
img_dict.dict_union(top_dict, path_dict)
- # add it to the paths, as a cross reference:
+ # add it to the paths, as a cross reference:
paths.append(path)
- # now create the final json file that combines the previous ones into a single usable object:
+ # now create the final json file that combines the previous ones into a single usable object:
i_json += 1
- # convert the subdict to .json
+ # convert the subdict to .json
subdict_as_json = json_from_dict(top_dict)
- # and write this out:
- json_file = '{}_{}{}'.format(file_name_no_ext, i_json, suffix)
- with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
- dir=tmp_file_dir, delete=False) as file_obj:
+ # and write this out:
+ json_file = '{}_{}{}'.format(file_name_no_ext, i_json, suffix)
+ with tempfile.NamedTemporaryFile('w', suffix='.json', prefix='imt_',
+ dir=tmp_file_dir, delete=False) as file_obj:
if compression:
file_obj.write(zlib.compress(subdict_as_json))
else:
file_obj.write(subdict_as_json)
tmp_file_path = file_obj.name
- # make a note of the outputs:
+ # make a note of the outputs:
out_files.append((tmp_file_path, json_file))
- return out_files
-
-[docs]def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None,
- style='horiz dropdowns', level_names=False,
- animated_level=None):
+ return out_files
+
+[docs]def write_js_placeholders(file_obj=None, dict_depth=None, selector_prefix=None,
+ style='horiz dropdowns', level_names=False,
+ animated_level=None):
'''
Writes the placeholders into the page body, for the javascript to manipulate
@@ -679,89 +681,89 @@ Source code for ImageMetaTag.webpage
* animated_level - if supplied, as a string, this will be used to label the animator buttons.
'''
- if selector_prefix is None:
+ if selector_prefix is None:
selector_prefix, _junk1, _junk2 = write_js_setup_defaults()
- apply_level_names = False
+ apply_level_names = False
if level_names:
if not isinstance(level_names, list):
- raise ValueError('level_names needs to be a list of length dict_depth')
+ raise ValueError('level_names needs to be a list of length dict_depth')
if len(level_names) != dict_depth:
- raise ValueError('level_names needs to be a list, of length dict_depth')
- apply_level_names = True
+ raise ValueError('level_names needs to be a list, of length dict_depth')
+ apply_level_names = True
else:
- apply_level_names = False
-
- if style == 'horiz dropdowns':
- file_obj.write('''
-<!-- Now for some placeholders for the scripts to put content -->
-<table border=0 cellspacing=0 cellpadding=0 width=99% align=center>
- <tr>
- <td>
- <font size=3>''')
- # a text label for the animator buttons:
+ apply_level_names = False
+
+ if style == 'horiz dropdowns':
+ file_obj.write('''
+<!-- Now for some placeholders for the scripts to put content -->
+<table border=0 cellspacing=0 cellpadding=0 width=99% align=center>
+ <tr>
+ <td>
+ <font size=3>''')
+ # a text label for the animator buttons:
if isinstance(animated_level, str):
- anim_label = '{}: '.format(animated_level)
+ anim_label = '{}: '.format(animated_level)
else:
- anim_label = ''
+ anim_label = ''
- # for each level of depth in the plot dictionary, add a span to hold the selector:
+ # for each level of depth in the plot dictionary, add a span to hold the selector:
if apply_level_names:
- # if we want labelled selectors, then write out
- # a table, with pairs of label, selector, in columns:
- file_obj.write('''
- <table border=0 cellspacing=0 cellpadding=0 style='border-spacing: 3px 0;'>
- <tr>
-''')
+ # if we want labelled selectors, then write out
+ # a table, with pairs of label, selector, in columns:
+ file_obj.write('''
+ <table border=0 cellspacing=0 cellpadding=0 style='border-spacing: 3px 0;'>
+ <tr>
+''')
for level in range(dict_depth):
- file_obj.write(' <td>{} </td>\n'.format(level_names[level]))
- file_obj.write(''' </tr>
- <tr>
-''')
+ file_obj.write(' <td>{} </td>\n'.format(level_names[level]))
+ file_obj.write(''' </tr>
+ <tr>
+''')
for level in range(dict_depth):
selp = selector_prefix + str(level)
- out_str = ' <td><span id="{}"> </span></td>\n'.format(selp)
+ out_str = ' <td><span id="{}"> </span></td>\n'.format(selp)
file_obj.write(out_str)
- file_obj.write(''' </tr>
-''')
- # add the placeholder for animators buttons:
- file_obj.write(''' <tr>
- <td colspan={}>
- {}<span id="animator1"> </span>
- <span id="animator2"> </span>
- </td>
- </tr>
- </table>
-'''.format(dict_depth, anim_label))
+ file_obj.write(''' </tr>
+''')
+ # add the placeholder for animators buttons:
+ file_obj.write(''' <tr>
+ <td colspan={}>
+ {}<span id="animator1"> </span>
+ <span id="animator2"> </span>
+ </td>
+ </tr>
+ </table>
+'''.format(dict_depth, anim_label))
else:
- # simply a set of spans, in a line:
+ # simply a set of spans, in a line:
for lev in range(dict_depth):
- file_obj.write('''
- <span id="%s%s"> </span>''' % (selector_prefix, lev))
- file_obj.write('\n <br>')
- # add the placeholder for animators buttons:
- file_obj.write('''
- {}<span id="animator1"> </span>
- <span id="animator2"> </span>
- <br>
- '''.format(anim_label))
-
- # now add somewhere for the image to go:
- file_obj.write(''' <div id="the_image">Please wait while the page is loading</div>
- <div id="the_url">....</div>''')
- # and finish off the placeholders:
- file_obj.write('''
- </font>
- </td>
- </tr>
-</table>
-
-''')
+ file_obj.write('''
+ <span id="%s%s"> </span>''' % (selector_prefix, lev))
+ file_obj.write('\n <br>')
+ # add the placeholder for animators buttons:
+ file_obj.write('''
+ {}<span id="animator1"> </span>
+ <span id="animator2"> </span>
+ <br>
+ '''.format(anim_label))
+
+ # now add somewhere for the image to go:
+ file_obj.write(''' <div id="the_image">Please wait while the page is loading</div>
+ <div id="the_url">....</div>''')
+ # and finish off the placeholders:
+ file_obj.write('''
+ </font>
+ </td>
+ </tr>
+</table>
+
+''')
else:
- raise ValueError('"%s" tyle of content placeholder not defined' % style)
-
-[docs]def copy_required_javascript(file_dir, style, compression=False, overwrite=True):
+ raise ValueError('"%s" tyle of content placeholder not defined' % style)
+
+[docs]def copy_required_javascript(file_dir, style, compression=False, overwrite=True):
'''
Copies the required javascript library to the directory
containing the required page (file_dir) for a given webpage style.
@@ -773,126 +775,126 @@ Source code for ImageMetaTag.webpage
with zlib, if compression=True.
'''
- if style == 'horiz dropdowns':
- imt_js_to_copy = 'imt_dropdown.js'
- # get this from the installed ImageMetaTag directory:
- file_src_dir = os.path.join(imt.__path__[0], 'javascript')
- first_line = '// ImageMetaTag dropdown menu scripting - vn{}\n'.format(imt.__version__)
+ if style == 'horiz dropdowns':
+ imt_js_to_copy = 'imt_dropdown.js'
+ # get this from the installed ImageMetaTag directory:
+ file_src_dir = os.path.join(imt.__path__[0], 'javascript')
+ first_line = '// ImageMetaTag dropdown menu scripting - vn{}\n'.format(imt.__version__)
else:
- raise ValueError('Javascript library not set up for style: {}'.format(style))
+ raise ValueError('Javascript library not set up for style: {}'.format(style))
if not os.path.isfile(os.path.join(file_dir, imt_js_to_copy)):
- # file isn't in target dir, so copy it:
+ # file isn't in target dir, so copy it:
shutil.copy(os.path.join(file_src_dir, imt_js_to_copy),
os.path.join(file_dir, imt_js_to_copy))
else:
- # the file is there, check it's right:
+ # the file is there, check it's right:
with open(os.path.join(file_dir, imt_js_to_copy)) as file_obj:
this_first_line = file_obj.readline()
if first_line == this_first_line:
- # the file is good, move on:
+ # the file is good, move on:
pass
else:
if overwrite:
shutil.copy(os.path.join(file_src_dir, imt_js_to_copy),
os.path.join(file_dir, imt_js_to_copy))
else:
- print '''File: {}/{} differs to the expected contents, but is
-not being overwritten. Your webpage may be broken!'''.format(file_dir, imt_js_to_copy)
+ print '''File: {}/{} differs to the expected contents, but is
+not being overwritten. Your webpage may be broken!'''.format(file_dir, imt_js_to_copy)
- # make a list of all the required javascript files
+ # make a list of all the required javascript files
js_files = [imt_js_to_copy]
- # now move on to javascript dependencies from the compression:
+ # now move on to javascript dependencies from the compression:
if compression:
js_to_copy = PAKO_JS_FILE
js_src = os.path.join(file_src_dir, js_to_copy)
js_dest = os.path.join(file_dir, js_to_copy)
- # if the file is already at destination, we're good:
+ # if the file is already at destination, we're good:
if os.path.isfile(js_dest):
pass
else:
- # is the required file in the javascript source directory:
+ # is the required file in the javascript source directory:
if not os.path.isfile(js_src):
- # we need to get the required javascript from source.
- #
- # if we have permission to write to teh file_src_dir then
- # try to do so. This means it's installed for all uses from this
- # install of ImageMetaTag:
+ # we need to get the required javascript from source.
+ #
+ # if we have permission to write to teh file_src_dir then
+ # try to do so. This means it's installed for all uses from this
+ # install of ImageMetaTag:
if os.access(file_src_dir, os.W_OK):
pako_to_dir = file_src_dir
- # now get pako:
+ # now get pako:
get_pako(pako_to_dir=pako_to_dir)
- # and copy it to where it's needed for this call:
+ # and copy it to where it's needed for this call:
shutil.copy(js_src, js_dest)
else:
- # put pako js file into the target dir. At least it will
- # be available for subsequent writes to that dir:
+ # put pako js file into the target dir. At least it will
+ # be available for subsequent writes to that dir:
pako_to_dir = file_dir
- # now get pako to that dir:
+ # now get pako to that dir:
get_pako(pako_to_dir=pako_to_dir)
else:
- # copy the file:
+ # copy the file:
shutil.copy(js_src, js_dest)
- # finally, make a note:
+ # finally, make a note:
js_files.append(js_to_copy)
- return js_files
-
-def get_pako(pako_to_dir=None):
+ return js_files
+
+def get_pako(pako_to_dir=None):
'''
Obtains the required pako javascript code from remote host, to a given
javascript directory. If the javascript dir is not supplied, then
the 'javascript' directory alongside the ImageMetaTag python code is used.
'''
import tarfile
- from urllib2 import urlopen
+ from urllib2 import urlopen
- # set up pako into the current imt_dir:
- if pako_to_dir is None:
- pako_to_dir = os.path.join(imt.__path__[0], 'javascript')
+ # set up pako into the current imt_dir:
+ if pako_to_dir is None:
+ pako_to_dir = os.path.join(imt.__path__[0], 'javascript')
- # Open the url
+ # Open the url
pako_urlopen = urlopen(PAKO_SOURE_TAR)
- print "downloading " + PAKO_SOURE_TAR
- # Open our local file for writing
- with tempfile.NamedTemporaryFile('w', suffix='.tar.gz', prefix='pako_',
- delete=False) as local_file:
+ print "downloading " + PAKO_SOURE_TAR
+ # Open our local file for writing
+ with tempfile.NamedTemporaryFile('w', suffix='.tar.gz', prefix='pako_',
+ delete=False) as local_file:
local_file.write(pako_urlopen.read())
targz_file = local_file.name
pako_urlopen.close()
- # now extract the file we need:
- with tarfile.open(name=targz_file, mode='r:gz') as tgz:
+ # now extract the file we need:
+ with tarfile.open(name=targz_file, mode='r:gz') as tgz:
if not tarfile.is_tarfile:
- raise ValueError('Downloaded pako tar.gz file cannot be read.')
+ raise ValueError('Downloaded pako tar.gz file cannot be read.')
else:
- target = 'pako-{}/dist/{}'.format(PAKO_RELEASE, PAKO_JS_FILE)
+ target = 'pako-{}/dist/{}'.format(PAKO_RELEASE, PAKO_JS_FILE)
target_file = tgz.extractfile(target)
if target_file:
- with open(os.path.join(pako_to_dir, PAKO_JS_FILE), 'w') as final_file:
+ with open(os.path.join(pako_to_dir, PAKO_JS_FILE), 'w') as final_file:
for line in target_file:
final_file.write(line)
os.remove(targz_file)
def _indent_up_one(ind):
- 'increases the indent level of an input ind by one'
+ 'increases the indent level of an input ind by one'
n_indents = len(ind) / LEN_INDENT
return INDENT * (n_indents + 1)
def _indent_down_one(ind):
- 'decreases the indent level of an input ind by one'
+ 'decreases the indent level of an input ind by one'
n_indents = len(ind) / LEN_INDENT
return INDENT * max(n_indents - 1, 0)
def _py_to_js_bool(py_bool):
- 'Converts a python boolean to a string, in javascript bool format (all lower case)'
- if py_bool is True:
- return 'true'
- elif py_bool is False:
- return 'false'
+ 'Converts a python boolean to a string, in javascript bool format (all lower case)'
+ if py_bool is True:
+ return 'true'
+ elif py_bool is False:
+ return 'false'
else:
- raise ValueError('input to _py_to_js_bool is not a boolean, it is: %s' % py_bool)
+ raise ValueError('input to _py_to_js_bool is not a boolean, it is: %s' % py_bool)
@@ -900,7 +902,7 @@ Source code for ImageMetaTag.webpage
-
+
Navigation
-
@@ -909,13 +911,14 @@
Navigation
-
modules |
- - ImageMetaTag 0.6.4 documentation »
- - Module code »
+ - ImageMetaTag 0.6.5 documentation »
+ - Module code »
+ - ImageMetaTag »
-
- © Copyright 2015-2017, British Crown Copyright.
- Created using Sphinx 1.4.8.
+
+ © Copyright 2015-2017, British Crown Copyright.
+ Created using Sphinx 1.2.2.
\ No newline at end of file
diff --git a/docs/build/html/_modules/index.html b/docs/build/html/_modules/index.html
index 5077ffc..36b4e58 100644
--- a/docs/build/html/_modules/index.html
+++ b/docs/build/html/_modules/index.html
@@ -6,7 +6,7 @@
- Overview: module code — ImageMetaTag 0.6.4 documentation
+ Overview: module code — ImageMetaTag 0.6.5 documentation
@@ -14,7 +14,7 @@
-
-
-
+
-
-
+
+
Navigation
-
@@ -37,22 +35,25 @@
Navigation
-
modules |
- - ImageMetaTag 0.6.4 documentation »
+ - ImageMetaTag 0.6.5 documentation »
-
+
-
+
Quick search
+
+ Enter search terms or a module, class or function name.
+
@@ -61,21 +62,20 @@ Quick search
-
+
All modules for which code is available
+
-
+
Navigation
-
@@ -84,12 +84,12 @@
Navigation
-
modules |
- - ImageMetaTag 0.6.4 documentation »
+ - ImageMetaTag 0.6.5 documentation »
-
- © Copyright 2015-2017, British Crown Copyright.
- Created using Sphinx 1.4.8.
+
+ © Copyright 2015-2017, British Crown Copyright.
+ Created using Sphinx 1.2.2.
\ No newline at end of file
diff --git a/docs/build/html/_sources/savefig.txt b/docs/build/html/_sources/savefig.txt
index 8b394d2..672bc9a 100644
--- a/docs/build/html/_sources/savefig.txt
+++ b/docs/build/html/_sources/savefig.txt
@@ -7,7 +7,7 @@ ImageMetaTag.savefig
The savefig wrapper
-------------------
-The main only function that is usaully needed from this module is savefig wrapper iteself:
+The only function that is usaully needed from this module is savefig wrapper iteself:
.. autofunction:: ImageMetaTag.savefig
diff --git a/docs/build/html/_sources/simple.txt b/docs/build/html/_sources/simple.txt
index 6e7717b..b9de0a7 100644
--- a/docs/build/html/_sources/simple.txt
+++ b/docs/build/html/_sources/simple.txt
@@ -21,12 +21,16 @@ While the code in test.py aims to test as much of the ImageMetaTag functionality
# import ImageMetaTag:
import ImageMetaTag as imt
- # we are going to use just a couple of things from the test.py routine:
+ # we are going to use just a couple of things from the test.py routine.
from test import get_webdir, define_imt_db, DATE_FORMAT_WWW
def __main__():
- # define the web page directory and image database using the same
- # functions in test, so they are consistent:
+ # define the web page directory and image database
+ # Any real application of an ImageDict web page would need to
+ # define the locations of the web page and database for its own
+ # requirements.
+ # In this case, we are using the same functions in test, to be sure that
+ # they are consistent.
webdir = get_webdir()
imt_db = define_imt_db()
diff --git a/docs/build/html/_sources/test.txt b/docs/build/html/_sources/test.txt
index 0f14df6..714f483 100644
--- a/docs/build/html/_sources/test.txt
+++ b/docs/build/html/_sources/test.txt
@@ -3,7 +3,7 @@
ImageMetaTag - test.py
========================================
-The test.py script includes tests for the main functionality of ImageMetaTag. It also acts as a demonstration of the code in action.
+The test.py script includes tests for the main functionality of ImageMetaTag. It also acts as a demonstration of the code in action on a set of very simple example plots of random data.
Running the command::
@@ -14,24 +14,19 @@ will create a set of plots of random data, create a number of :class:`ImageMetaT
The test.py script is not a part of the ImageMetaTag module, it is written to import the module and use it instead.
-.. warning::
-
- Currently these links only work within the Met Office
-
The webpages it produces can be viewed here:
- * A basic page presenting the plots: http://www-nwp/~freb/ImageMetaTagTest/page.html
- * A basic page, where the metadata processing was parallelised: http://www-nwp/~freb/ImageMetaTagTest/page_para.html
- * A page where multiple images are presented at the same time: http://www-nwp/~freb/ImageMetaTagTest/page_multi.html
-
+ * A basic page presenting the plots: http://gws-access.ceda.ac.uk/public/mo_forecasts/test/ImageMetaTagTest/page.html
+ * A basic page, where the metadata processing was parallelised: http://gws-access.ceda.ac.uk/public/mo_forecasts/test/ImageMetaTagTest/page_para.html
+ * A page where multiple images are presented at the same time: http://gws-access.ceda.ac.uk/public/mo_forecasts/test/ImageMetaTagTest/page_multi.html
The test.py script can be run without re-plotting the random data by::
python test.py --skip-plotting
-As a scalability test, once the images have been produced and wepages created, the test.py script also creates a much larger :class:`ImageMetaTag.ImageDict` with 9 factorial (9! = 362880) members. This process only mimics the metadata, it does not actually create the plots!
+As a scalability test, once the images have been produced and wepages created, the test.py script also creates a moderately large :class:`ImageMetaTag.ImageDict` with 9 factorial (9! = 362880) members. This process only mimics the metadata, it does not actually create the plots! It results in a web page which is deliberately empty: http://gws-access.ceda.ac.uk/public/mo_forecasts/test/ImageMetaTagTest/biggus_pageus.html
This test can be skipped by running the test as::
python test.py --no-big-dict
-
+
diff --git a/docs/build/html/_static/basic.css b/docs/build/html/_static/basic.css
index 0b79414..967e36c 100644
--- a/docs/build/html/_static/basic.css
+++ b/docs/build/html/_static/basic.css
@@ -4,7 +4,7 @@
*
* Sphinx stylesheet -- basic theme.
*
- * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -52,8 +52,6 @@ div.sphinxsidebar {
width: 230px;
margin-left: -100%;
font-size: 90%;
- word-wrap: break-word;
- overflow-wrap : break-word;
}
div.sphinxsidebar ul {
@@ -85,6 +83,10 @@ div.sphinxsidebar #searchbox input[type="text"] {
width: 170px;
}
+div.sphinxsidebar #searchbox input[type="submit"] {
+ width: 30px;
+}
+
img {
border: 0;
max-width: 100%;
@@ -185,13 +187,6 @@ div.genindex-jumpbox {
/* -- general body styles --------------------------------------------------- */
-div.body p, div.body dd, div.body li, div.body blockquote {
- -moz-hyphens: auto;
- -ms-hyphens: auto;
- -webkit-hyphens: auto;
- hyphens: auto;
-}
-
a.headerlink {
visibility: hidden;
}
@@ -202,10 +197,7 @@ h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
-dt:hover > a.headerlink,
-caption:hover > a.headerlink,
-p.caption:hover > a.headerlink,
-div.code-block-caption:hover > a.headerlink {
+dt:hover > a.headerlink {
visibility: visible;
}
@@ -322,13 +314,6 @@ table.docutils {
border-collapse: collapse;
}
-table caption span.caption-number {
- font-style: italic;
-}
-
-table caption span.caption-text {
-}
-
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
@@ -359,25 +344,6 @@ table.citation td {
border-bottom: none;
}
-/* -- figures --------------------------------------------------------------- */
-
-div.figure {
- margin: 0.5em;
- padding: 0.5em;
-}
-
-div.figure p.caption {
- padding: 0.3em;
-}
-
-div.figure p.caption span.caption-number {
- font-style: italic;
-}
-
-div.figure p.caption span.caption-text {
-}
-
-
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
@@ -440,10 +406,6 @@ dl.glossary dt {
font-size: 1.3em;
}
-.sig-paren {
- font-size: larger;
-}
-
.versionmodified {
font-style: italic;
}
@@ -494,13 +456,6 @@ pre {
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
-span.pre {
- -moz-hyphens: none;
- -ms-hyphens: none;
- -webkit-hyphens: none;
- hyphens: none;
-}
-
td.linenos pre {
padding: 5px 0px;
border: 0;
@@ -516,51 +471,22 @@ table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
-div.code-block-caption {
- padding: 2px 5px;
- font-size: small;
-}
-
-div.code-block-caption code {
- background-color: transparent;
-}
-
-div.code-block-caption + div > div.highlight > pre {
- margin-top: 0;
-}
-
-div.code-block-caption span.caption-number {
- padding: 0.1em 0.3em;
- font-style: italic;
-}
-
-div.code-block-caption span.caption-text {
-}
-
-div.literal-block-wrapper {
- padding: 1em 1em 0;
-}
-
-div.literal-block-wrapper div.highlight {
- margin: 0;
-}
-
-code.descname {
+tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
-code.descclassname {
+tt.descclassname {
background-color: transparent;
}
-code.xref, a code {
+tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
-h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
+h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
diff --git a/docs/build/html/_static/doctools.js b/docs/build/html/_static/doctools.js
index 8163495..c5455c9 100644
--- a/docs/build/html/_static/doctools.js
+++ b/docs/build/html/_static/doctools.js
@@ -4,7 +4,7 @@
*
* Sphinx JavaScript utilities for all documentation.
*
- * :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS.
+ * :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
@@ -91,30 +91,6 @@ jQuery.fn.highlightText = function(text, className) {
});
};
-/*
- * backward compatibility for jQuery.browser
- * This will be supported until firefox bug is fixed.
- */
-if (!jQuery.browser) {
- jQuery.uaMatch = function(ua) {
- ua = ua.toLowerCase();
-
- var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
- /(webkit)[ \/]([\w.]+)/.exec(ua) ||
- /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
- /(msie) ([\w.]+)/.exec(ua) ||
- ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
- [];
-
- return {
- browser: match[ 1 ] || "",
- version: match[ 2 ] || "0"
- };
- };
- jQuery.browser = {};
- jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
-}
-
/**
* Small JavaScript module for the documentation.
*/
@@ -124,7 +100,6 @@ var Documentation = {
this.fixFirefoxAnchorBug();
this.highlightSearchWords();
this.initIndexTable();
-
},
/**
@@ -177,10 +152,9 @@ var Documentation = {
/**
* workaround a firefox stupidity
- * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075
*/
fixFirefoxAnchorBug : function() {
- if (document.location.hash)
+ if (document.location.hash && $.browser.mozilla)
window.setTimeout(function() {
document.location.href += '';
}, 10);
@@ -253,29 +227,6 @@ var Documentation = {
});
var url = parts.join('/');
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
- },
-
- initOnKeyListeners: function() {
- $(document).keyup(function(event) {
- var activeElementType = document.activeElement.tagName;
- // don't navigate when in search box or textarea
- if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT') {
- switch (event.keyCode) {
- case 37: // left
- var prevHref = $('link[rel="prev"]').prop('href');
- if (prevHref) {
- window.location.href = prevHref;
- return false;
- }
- case 39: // right
- var nextHref = $('link[rel="next"]').prop('href');
- if (nextHref) {
- window.location.href = nextHref;
- return false;
- }
- }
- }
- });
}
};
@@ -284,4 +235,4 @@ _ = Documentation.gettext;
$(document).ready(function() {
Documentation.init();
-});
\ No newline at end of file
+});
diff --git a/docs/build/html/_static/down-pressed.png b/docs/build/html/_static/down-pressed.png
index 7c30d00..6f7ad78 100644
Binary files a/docs/build/html/_static/down-pressed.png and b/docs/build/html/_static/down-pressed.png differ
diff --git a/docs/build/html/_static/down.png b/docs/build/html/_static/down.png
index f48098a..3003a88 100644
Binary files a/docs/build/html/_static/down.png and b/docs/build/html/_static/down.png differ
diff --git a/docs/build/html/_static/file.png b/docs/build/html/_static/file.png
index 254c60b..d18082e 100644
Binary files a/docs/build/html/_static/file.png and b/docs/build/html/_static/file.png differ
diff --git a/docs/build/html/_static/jquery-1.11.1.js b/docs/build/html/_static/jquery-1.11.1.js
deleted file mode 100644
index d4b67f7..0000000
--- a/docs/build/html/_static/jquery-1.11.1.js
+++ /dev/null
@@ -1,10308 +0,0 @@
-/*!
- * jQuery JavaScript Library v1.11.1
- * http://jquery.com/
- *
- * Includes Sizzle.js
- * http://sizzlejs.com/
- *
- * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-05-01T17:42Z
- */
-
-(function( global, factory ) {
-
- if ( typeof module === "object" && typeof module.exports === "object" ) {
- // For CommonJS and CommonJS-like environments where a proper window is present,
- // execute the factory and get jQuery
- // For environments that do not inherently posses a window with a document
- // (such as Node.js), expose a jQuery-making factory as module.exports
- // This accentuates the need for the creation of a real window
- // e.g. var jQuery = require("jquery")(window);
- // See ticket #14549 for more info
- module.exports = global.document ?
- factory( global, true ) :
- function( w ) {
- if ( !w.document ) {
- throw new Error( "jQuery requires a window with a document" );
- }
- return factory( w );
- };
- } else {
- factory( global );
- }
-
-// Pass this if window is not defined yet
-}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-// Can't do this because several apps including ASP.NET trace
-// the stack via arguments.caller.callee and Firefox dies if
-// you try to trace through "use strict" call chains. (#13335)
-// Support: Firefox 18+
-//
-
-var deletedIds = [];
-
-var slice = deletedIds.slice;
-
-var concat = deletedIds.concat;
-
-var push = deletedIds.push;
-
-var indexOf = deletedIds.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var support = {};
-
-
-
-var
- version = "1.11.1",
-
- // Define a local copy of jQuery
- jQuery = function( selector, context ) {
- // The jQuery object is actually just the init constructor 'enhanced'
- // Need init if jQuery is called (just allow error to be thrown if not included)
- return new jQuery.fn.init( selector, context );
- },
-
- // Support: Android<4.1, IE<9
- // Make sure we trim BOM and NBSP
- rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
-
- // Matches dashed string for camelizing
- rmsPrefix = /^-ms-/,
- rdashAlpha = /-([\da-z])/gi,
-
- // Used by jQuery.camelCase as callback to replace()
- fcamelCase = function( all, letter ) {
- return letter.toUpperCase();
- };
-
-jQuery.fn = jQuery.prototype = {
- // The current version of jQuery being used
- jquery: version,
-
- constructor: jQuery,
-
- // Start with an empty selector
- selector: "",
-
- // The default length of a jQuery object is 0
- length: 0,
-
- toArray: function() {
- return slice.call( this );
- },
-
- // Get the Nth element in the matched element set OR
- // Get the whole matched element set as a clean array
- get: function( num ) {
- return num != null ?
-
- // Return just the one element from the set
- ( num < 0 ? this[ num + this.length ] : this[ num ] ) :
-
- // Return all the elements in a clean array
- slice.call( this );
- },
-
- // Take an array of elements and push it onto the stack
- // (returning the new matched element set)
- pushStack: function( elems ) {
-
- // Build a new jQuery matched element set
- var ret = jQuery.merge( this.constructor(), elems );
-
- // Add the old object onto the stack (as a reference)
- ret.prevObject = this;
- ret.context = this.context;
-
- // Return the newly-formed element set
- return ret;
- },
-
- // Execute a callback for every element in the matched set.
- // (You can seed the arguments with an array of args, but this is
- // only used internally.)
- each: function( callback, args ) {
- return jQuery.each( this, callback, args );
- },
-
- map: function( callback ) {
- return this.pushStack( jQuery.map(this, function( elem, i ) {
- return callback.call( elem, i, elem );
- }));
- },
-
- slice: function() {
- return this.pushStack( slice.apply( this, arguments ) );
- },
-
- first: function() {
- return this.eq( 0 );
- },
-
- last: function() {
- return this.eq( -1 );
- },
-
- eq: function( i ) {
- var len = this.length,
- j = +i + ( i < 0 ? len : 0 );
- return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
- },
-
- end: function() {
- return this.prevObject || this.constructor(null);
- },
-
- // For internal use only.
- // Behaves like an Array's method, not like a jQuery method.
- push: push,
- sort: deletedIds.sort,
- splice: deletedIds.splice
-};
-
-jQuery.extend = jQuery.fn.extend = function() {
- var src, copyIsArray, copy, name, options, clone,
- target = arguments[0] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- // Handle a deep copy situation
- if ( typeof target === "boolean" ) {
- deep = target;
-
- // skip the boolean and the target
- target = arguments[ i ] || {};
- i++;
- }
-
- // Handle case when target is a string or something (possible in deep copy)
- if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
- target = {};
- }
-
- // extend jQuery itself if only one argument is passed
- if ( i === length ) {
- target = this;
- i--;
- }
-
- for ( ; i < length; i++ ) {
- // Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
- // Extend the base object
- for ( name in options ) {
- src = target[ name ];
- copy = options[ name ];
-
- // Prevent never-ending loop
- if ( target === copy ) {
- continue;
- }
-
- // Recurse if we're merging plain objects or arrays
- if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
- if ( copyIsArray ) {
- copyIsArray = false;
- clone = src && jQuery.isArray(src) ? src : [];
-
- } else {
- clone = src && jQuery.isPlainObject(src) ? src : {};
- }
-
- // Never move original objects, clone them
- target[ name ] = jQuery.extend( deep, clone, copy );
-
- // Don't bring in undefined values
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
-
- // Return the modified object
- return target;
-};
-
-jQuery.extend({
- // Unique for each copy of jQuery on the page
- expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
-
- // Assume jQuery is ready without the ready module
- isReady: true,
-
- error: function( msg ) {
- throw new Error( msg );
- },
-
- noop: function() {},
-
- // See test/unit/core.js for details concerning isFunction.
- // Since version 1.3, DOM methods and functions like alert
- // aren't supported. They return false on IE (#2968).
- isFunction: function( obj ) {
- return jQuery.type(obj) === "function";
- },
-
- isArray: Array.isArray || function( obj ) {
- return jQuery.type(obj) === "array";
- },
-
- isWindow: function( obj ) {
- /* jshint eqeqeq: false */
- return obj != null && obj == obj.window;
- },
-
- isNumeric: function( obj ) {
- // parseFloat NaNs numeric-cast false positives (null|true|false|"")
- // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
- // subtraction forces infinities to NaN
- return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
- },
-
- isEmptyObject: function( obj ) {
- var name;
- for ( name in obj ) {
- return false;
- }
- return true;
- },
-
- isPlainObject: function( obj ) {
- var key;
-
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- try {
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
- return false;
- }
- } catch ( e ) {
- // IE8,9 Will throw exceptions on certain host objects #9897
- return false;
- }
-
- // Support: IE<9
- // Handle iteration over inherited properties before own properties.
- if ( support.ownLast ) {
- for ( key in obj ) {
- return hasOwn.call( obj, key );
- }
- }
-
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
- for ( key in obj ) {}
-
- return key === undefined || hasOwn.call( obj, key );
- },
-
- type: function( obj ) {
- if ( obj == null ) {
- return obj + "";
- }
- return typeof obj === "object" || typeof obj === "function" ?
- class2type[ toString.call(obj) ] || "object" :
- typeof obj;
- },
-
- // Evaluates a script in a global context
- // Workarounds based on findings by Jim Driscoll
- // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
- globalEval: function( data ) {
- if ( data && jQuery.trim( data ) ) {
- // We use execScript on Internet Explorer
- // We use an anonymous function so that context is window
- // rather than jQuery in Firefox
- ( window.execScript || function( data ) {
- window[ "eval" ].call( window, data );
- } )( data );
- }
- },
-
- // Convert dashed to camelCase; used by the css and data modules
- // Microsoft forgot to hump their vendor prefix (#9572)
- camelCase: function( string ) {
- return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
- },
-
- nodeName: function( elem, name ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
- },
-
- // args is for internal usage only
- each: function( obj, callback, args ) {
- var value,
- i = 0,
- length = obj.length,
- isArray = isArraylike( obj );
-
- if ( args ) {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.apply( obj[ i ], args );
-
- if ( value === false ) {
- break;
- }
- }
- }
-
- // A special, fast, case for the most common use of each
- } else {
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- value = callback.call( obj[ i ], i, obj[ i ] );
-
- if ( value === false ) {
- break;
- }
- }
- }
- }
-
- return obj;
- },
-
- // Support: Android<4.1, IE<9
- trim: function( text ) {
- return text == null ?
- "" :
- ( text + "" ).replace( rtrim, "" );
- },
-
- // results is for internal usage only
- makeArray: function( arr, results ) {
- var ret = results || [];
-
- if ( arr != null ) {
- if ( isArraylike( Object(arr) ) ) {
- jQuery.merge( ret,
- typeof arr === "string" ?
- [ arr ] : arr
- );
- } else {
- push.call( ret, arr );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, arr, i ) {
- var len;
-
- if ( arr ) {
- if ( indexOf ) {
- return indexOf.call( arr, elem, i );
- }
-
- len = arr.length;
- i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
-
- for ( ; i < len; i++ ) {
- // Skip accessing in sparse arrays
- if ( i in arr && arr[ i ] === elem ) {
- return i;
- }
- }
- }
-
- return -1;
- },
-
- merge: function( first, second ) {
- var len = +second.length,
- j = 0,
- i = first.length;
-
- while ( j < len ) {
- first[ i++ ] = second[ j++ ];
- }
-
- // Support: IE<9
- // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists)
- if ( len !== len ) {
- while ( second[j] !== undefined ) {
- first[ i++ ] = second[ j++ ];
- }
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, invert ) {
- var callbackInverse,
- matches = [],
- i = 0,
- length = elems.length,
- callbackExpect = !invert;
-
- // Go through the array, only saving the items
- // that pass the validator function
- for ( ; i < length; i++ ) {
- callbackInverse = !callback( elems[ i ], i );
- if ( callbackInverse !== callbackExpect ) {
- matches.push( elems[ i ] );
- }
- }
-
- return matches;
- },
-
- // arg is for internal usage only
- map: function( elems, callback, arg ) {
- var value,
- i = 0,
- length = elems.length,
- isArray = isArraylike( elems ),
- ret = [];
-
- // Go through the array, translating each of the items to their new values
- if ( isArray ) {
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret.push( value );
- }
- }
-
- // Go through every key on the object,
- } else {
- for ( i in elems ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret.push( value );
- }
- }
- }
-
- // Flatten any nested arrays
- return concat.apply( [], ret );
- },
-
- // A global GUID counter for objects
- guid: 1,
-
- // Bind a function to a context, optionally partially applying any
- // arguments.
- proxy: function( fn, context ) {
- var args, proxy, tmp;
-
- if ( typeof context === "string" ) {
- tmp = fn[ context ];
- context = fn;
- fn = tmp;
- }
-
- // Quick check to determine if target is callable, in the spec
- // this throws a TypeError, but we will just return undefined.
- if ( !jQuery.isFunction( fn ) ) {
- return undefined;
- }
-
- // Simulated bind
- args = slice.call( arguments, 2 );
- proxy = function() {
- return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
- };
-
- // Set the guid of unique handler to the same of original handler, so it can be removed
- proxy.guid = fn.guid = fn.guid || jQuery.guid++;
-
- return proxy;
- },
-
- now: function() {
- return +( new Date() );
- },
-
- // jQuery.support is not used in Core but other projects attach their
- // properties to it so it needs to exist.
- support: support
-});
-
-// Populate the class2type map
-jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
-});
-
-function isArraylike( obj ) {
- var length = obj.length,
- type = jQuery.type( obj );
-
- if ( type === "function" || jQuery.isWindow( obj ) ) {
- return false;
- }
-
- if ( obj.nodeType === 1 && length ) {
- return true;
- }
-
- return type === "array" || length === 0 ||
- typeof length === "number" && length > 0 && ( length - 1 ) in obj;
-}
-var Sizzle =
-/*!
- * Sizzle CSS Selector Engine v1.10.19
- * http://sizzlejs.com/
- *
- * Copyright 2013 jQuery Foundation, Inc. and other contributors
- * Released under the MIT license
- * http://jquery.org/license
- *
- * Date: 2014-04-18
- */
-(function( window ) {
-
-var i,
- support,
- Expr,
- getText,
- isXML,
- tokenize,
- compile,
- select,
- outermostContext,
- sortInput,
- hasDuplicate,
-
- // Local document vars
- setDocument,
- document,
- docElem,
- documentIsHTML,
- rbuggyQSA,
- rbuggyMatches,
- matches,
- contains,
-
- // Instance-specific data
- expando = "sizzle" + -(new Date()),
- preferredDoc = window.document,
- dirruns = 0,
- done = 0,
- classCache = createCache(),
- tokenCache = createCache(),
- compilerCache = createCache(),
- sortOrder = function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- }
- return 0;
- },
-
- // General-purpose constants
- strundefined = typeof undefined,
- MAX_NEGATIVE = 1 << 31,
-
- // Instance methods
- hasOwn = ({}).hasOwnProperty,
- arr = [],
- pop = arr.pop,
- push_native = arr.push,
- push = arr.push,
- slice = arr.slice,
- // Use a stripped-down indexOf if we can't use a native one
- indexOf = arr.indexOf || function( elem ) {
- var i = 0,
- len = this.length;
- for ( ; i < len; i++ ) {
- if ( this[i] === elem ) {
- return i;
- }
- }
- return -1;
- },
-
- booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
-
- // Regular expressions
-
- // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
- whitespace = "[\\x20\\t\\r\\n\\f]",
- // http://www.w3.org/TR/css3-syntax/#characters
- characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
-
- // Loosely modeled on CSS identifier characters
- // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
- // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
- identifier = characterEncoding.replace( "w", "w#" ),
-
- // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
- attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
- // Operator (capture 2)
- "*([*^$|!~]?=)" + whitespace +
- // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
- "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
- "*\\]",
-
- pseudos = ":(" + characterEncoding + ")(?:\\((" +
- // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
- // 1. quoted (capture 3; capture 4 or capture 5)
- "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
- // 2. simple (capture 6)
- "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
- // 3. anything else (capture 2)
- ".*" +
- ")\\)|)",
-
- // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
- rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
-
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
-
- rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
-
- rpseudo = new RegExp( pseudos ),
- ridentifier = new RegExp( "^" + identifier + "$" ),
-
- matchExpr = {
- "ID": new RegExp( "^#(" + characterEncoding + ")" ),
- "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
- "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
- "ATTR": new RegExp( "^" + attributes ),
- "PSEUDO": new RegExp( "^" + pseudos ),
- "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
- "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
- "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
- // For use in libraries implementing .is()
- // We use this for POS matching in `select`
- "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
- whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
- },
-
- rinputs = /^(?:input|select|textarea|button)$/i,
- rheader = /^h\d$/i,
-
- rnative = /^[^{]+\{\s*\[native \w/,
-
- // Easily-parseable/retrievable ID or TAG or CLASS selectors
- rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
- rsibling = /[+~]/,
- rescape = /'|\\/g,
-
- // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
- runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
- funescape = function( _, escaped, escapedWhitespace ) {
- var high = "0x" + escaped - 0x10000;
- // NaN means non-codepoint
- // Support: Firefox<24
- // Workaround erroneous numeric interpretation of +"0x"
- return high !== high || escapedWhitespace ?
- escaped :
- high < 0 ?
- // BMP codepoint
- String.fromCharCode( high + 0x10000 ) :
- // Supplemental Plane codepoint (surrogate pair)
- String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
- };
-
-// Optimize for push.apply( _, NodeList )
-try {
- push.apply(
- (arr = slice.call( preferredDoc.childNodes )),
- preferredDoc.childNodes
- );
- // Support: Android<4.0
- // Detect silently failing push.apply
- arr[ preferredDoc.childNodes.length ].nodeType;
-} catch ( e ) {
- push = { apply: arr.length ?
-
- // Leverage slice if possible
- function( target, els ) {
- push_native.apply( target, slice.call(els) );
- } :
-
- // Support: IE<9
- // Otherwise append directly
- function( target, els ) {
- var j = target.length,
- i = 0;
- // Can't trust NodeList.length
- while ( (target[j++] = els[i++]) ) {}
- target.length = j - 1;
- }
- };
-}
-
-function Sizzle( selector, context, results, seed ) {
- var match, elem, m, nodeType,
- // QSA vars
- i, groups, old, nid, newContext, newSelector;
-
- if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
- setDocument( context );
- }
-
- context = context || document;
- results = results || [];
-
- if ( !selector || typeof selector !== "string" ) {
- return results;
- }
-
- if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
- return [];
- }
-
- if ( documentIsHTML && !seed ) {
-
- // Shortcuts
- if ( (match = rquickExpr.exec( selector )) ) {
- // Speed-up: Sizzle("#ID")
- if ( (m = match[1]) ) {
- if ( nodeType === 9 ) {
- elem = context.getElementById( m );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document (jQuery #6963)
- if ( elem && elem.parentNode ) {
- // Handle the case where IE, Opera, and Webkit return items
- // by name instead of ID
- if ( elem.id === m ) {
- results.push( elem );
- return results;
- }
- } else {
- return results;
- }
- } else {
- // Context is not a document
- if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
- contains( context, elem ) && elem.id === m ) {
- results.push( elem );
- return results;
- }
- }
-
- // Speed-up: Sizzle("TAG")
- } else if ( match[2] ) {
- push.apply( results, context.getElementsByTagName( selector ) );
- return results;
-
- // Speed-up: Sizzle(".CLASS")
- } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
- push.apply( results, context.getElementsByClassName( m ) );
- return results;
- }
- }
-
- // QSA path
- if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
- nid = old = expando;
- newContext = context;
- newSelector = nodeType === 9 && selector;
-
- // qSA works strangely on Element-rooted queries
- // We can work around this by specifying an extra ID on the root
- // and working up from there (Thanks to Andrew Dupont for the technique)
- // IE 8 doesn't work on object elements
- if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
- groups = tokenize( selector );
-
- if ( (old = context.getAttribute("id")) ) {
- nid = old.replace( rescape, "\\$&" );
- } else {
- context.setAttribute( "id", nid );
- }
- nid = "[id='" + nid + "'] ";
-
- i = groups.length;
- while ( i-- ) {
- groups[i] = nid + toSelector( groups[i] );
- }
- newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
- newSelector = groups.join(",");
- }
-
- if ( newSelector ) {
- try {
- push.apply( results,
- newContext.querySelectorAll( newSelector )
- );
- return results;
- } catch(qsaError) {
- } finally {
- if ( !old ) {
- context.removeAttribute("id");
- }
- }
- }
- }
- }
-
- // All others
- return select( selector.replace( rtrim, "$1" ), context, results, seed );
-}
-
-/**
- * Create key-value caches of limited size
- * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
- * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
- * deleting the oldest entry
- */
-function createCache() {
- var keys = [];
-
- function cache( key, value ) {
- // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
- if ( keys.push( key + " " ) > Expr.cacheLength ) {
- // Only keep the most recent entries
- delete cache[ keys.shift() ];
- }
- return (cache[ key + " " ] = value);
- }
- return cache;
-}
-
-/**
- * Mark a function for special use by Sizzle
- * @param {Function} fn The function to mark
- */
-function markFunction( fn ) {
- fn[ expando ] = true;
- return fn;
-}
-
-/**
- * Support testing using an element
- * @param {Function} fn Passed the created div and expects a boolean result
- */
-function assert( fn ) {
- var div = document.createElement("div");
-
- try {
- return !!fn( div );
- } catch (e) {
- return false;
- } finally {
- // Remove from its parent by default
- if ( div.parentNode ) {
- div.parentNode.removeChild( div );
- }
- // release memory in IE
- div = null;
- }
-}
-
-/**
- * Adds the same handler for all of the specified attrs
- * @param {String} attrs Pipe-separated list of attributes
- * @param {Function} handler The method that will be applied
- */
-function addHandle( attrs, handler ) {
- var arr = attrs.split("|"),
- i = attrs.length;
-
- while ( i-- ) {
- Expr.attrHandle[ arr[i] ] = handler;
- }
-}
-
-/**
- * Checks document order of two siblings
- * @param {Element} a
- * @param {Element} b
- * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
- */
-function siblingCheck( a, b ) {
- var cur = b && a,
- diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
- ( ~b.sourceIndex || MAX_NEGATIVE ) -
- ( ~a.sourceIndex || MAX_NEGATIVE );
-
- // Use IE sourceIndex if available on both nodes
- if ( diff ) {
- return diff;
- }
-
- // Check if b follows a
- if ( cur ) {
- while ( (cur = cur.nextSibling) ) {
- if ( cur === b ) {
- return -1;
- }
- }
- }
-
- return a ? 1 : -1;
-}
-
-/**
- * Returns a function to use in pseudos for input types
- * @param {String} type
- */
-function createInputPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === type;
- };
-}
-
-/**
- * Returns a function to use in pseudos for buttons
- * @param {String} type
- */
-function createButtonPseudo( type ) {
- return function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return (name === "input" || name === "button") && elem.type === type;
- };
-}
-
-/**
- * Returns a function to use in pseudos for positionals
- * @param {Function} fn
- */
-function createPositionalPseudo( fn ) {
- return markFunction(function( argument ) {
- argument = +argument;
- return markFunction(function( seed, matches ) {
- var j,
- matchIndexes = fn( [], seed.length, argument ),
- i = matchIndexes.length;
-
- // Match elements found at the specified indexes
- while ( i-- ) {
- if ( seed[ (j = matchIndexes[i]) ] ) {
- seed[j] = !(matches[j] = seed[j]);
- }
- }
- });
- });
-}
-
-/**
- * Checks a node for validity as a Sizzle context
- * @param {Element|Object=} context
- * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
- */
-function testContext( context ) {
- return context && typeof context.getElementsByTagName !== strundefined && context;
-}
-
-// Expose support vars for convenience
-support = Sizzle.support = {};
-
-/**
- * Detects XML nodes
- * @param {Element|Object} elem An element or a document
- * @returns {Boolean} True iff elem is a non-HTML XML node
- */
-isXML = Sizzle.isXML = function( elem ) {
- // documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
- var documentElement = elem && (elem.ownerDocument || elem).documentElement;
- return documentElement ? documentElement.nodeName !== "HTML" : false;
-};
-
-/**
- * Sets document-related variables once based on the current document
- * @param {Element|Object} [doc] An element or document object to use to set the document
- * @returns {Object} Returns the current document
- */
-setDocument = Sizzle.setDocument = function( node ) {
- var hasCompare,
- doc = node ? node.ownerDocument || node : preferredDoc,
- parent = doc.defaultView;
-
- // If no document and documentElement is available, return
- if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
- return document;
- }
-
- // Set our document
- document = doc;
- docElem = doc.documentElement;
-
- // Support tests
- documentIsHTML = !isXML( doc );
-
- // Support: IE>8
- // If iframe document is assigned to "document" variable and if iframe has been reloaded,
- // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
- // IE6-8 do not support the defaultView property so parent will be undefined
- if ( parent && parent !== parent.top ) {
- // IE11 does not have attachEvent, so all must suffer
- if ( parent.addEventListener ) {
- parent.addEventListener( "unload", function() {
- setDocument();
- }, false );
- } else if ( parent.attachEvent ) {
- parent.attachEvent( "onunload", function() {
- setDocument();
- });
- }
- }
-
- /* Attributes
- ---------------------------------------------------------------------- */
-
- // Support: IE<8
- // Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
- support.attributes = assert(function( div ) {
- div.className = "i";
- return !div.getAttribute("className");
- });
-
- /* getElement(s)By*
- ---------------------------------------------------------------------- */
-
- // Check if getElementsByTagName("*") returns only elements
- support.getElementsByTagName = assert(function( div ) {
- div.appendChild( doc.createComment("") );
- return !div.getElementsByTagName("*").length;
- });
-
- // Check if getElementsByClassName can be trusted
- support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
- div.innerHTML = "";
-
- // Support: Safari<4
- // Catch class over-caching
- div.firstChild.className = "i";
- // Support: Opera<10
- // Catch gEBCN failure to find non-leading classes
- return div.getElementsByClassName("i").length === 2;
- });
-
- // Support: IE<10
- // Check if getElementById returns elements by name
- // The broken getElementById methods don't pick up programatically-set names,
- // so use a roundabout getElementsByName test
- support.getById = assert(function( div ) {
- docElem.appendChild( div ).id = expando;
- return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
- });
-
- // ID find and filter
- if ( support.getById ) {
- Expr.find["ID"] = function( id, context ) {
- if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
- var m = context.getElementById( id );
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- return m && m.parentNode ? [ m ] : [];
- }
- };
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- return elem.getAttribute("id") === attrId;
- };
- };
- } else {
- // Support: IE6/7
- // getElementById is not reliable as a find shortcut
- delete Expr.find["ID"];
-
- Expr.filter["ID"] = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
- return node && node.value === attrId;
- };
- };
- }
-
- // Tag
- Expr.find["TAG"] = support.getElementsByTagName ?
- function( tag, context ) {
- if ( typeof context.getElementsByTagName !== strundefined ) {
- return context.getElementsByTagName( tag );
- }
- } :
- function( tag, context ) {
- var elem,
- tmp = [],
- i = 0,
- results = context.getElementsByTagName( tag );
-
- // Filter out possible comments
- if ( tag === "*" ) {
- while ( (elem = results[i++]) ) {
- if ( elem.nodeType === 1 ) {
- tmp.push( elem );
- }
- }
-
- return tmp;
- }
- return results;
- };
-
- // Class
- Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
- if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
- return context.getElementsByClassName( className );
- }
- };
-
- /* QSA/matchesSelector
- ---------------------------------------------------------------------- */
-
- // QSA and matchesSelector support
-
- // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
- rbuggyMatches = [];
-
- // qSa(:focus) reports false when true (Chrome 21)
- // We allow this because of a bug in IE8/9 that throws an error
- // whenever `document.activeElement` is accessed on an iframe
- // So, we allow :focus to pass through QSA all the time to avoid the IE error
- // See http://bugs.jquery.com/ticket/13378
- rbuggyQSA = [];
-
- if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
- // Build QSA regex
- // Regex strategy adopted from Diego Perini
- assert(function( div ) {
- // Select is set to empty string on purpose
- // This is to test IE's treatment of not explicitly
- // setting a boolean content attribute,
- // since its presence should be enough
- // http://bugs.jquery.com/ticket/12359
- div.innerHTML = "";
-
- // Support: IE8, Opera 11-12.16
- // Nothing should be selected when empty strings follow ^= or $= or *=
- // The test attribute must be unknown in Opera but "safe" for WinRT
- // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
- if ( div.querySelectorAll("[msallowclip^='']").length ) {
- rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
- }
-
- // Support: IE8
- // Boolean attributes and "value" are not treated correctly
- if ( !div.querySelectorAll("[selected]").length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
- }
-
- // Webkit/Opera - :checked should return selected option elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":checked").length ) {
- rbuggyQSA.push(":checked");
- }
- });
-
- assert(function( div ) {
- // Support: Windows 8 Native Apps
- // The type and name attributes are restricted during .innerHTML assignment
- var input = doc.createElement("input");
- input.setAttribute( "type", "hidden" );
- div.appendChild( input ).setAttribute( "name", "D" );
-
- // Support: IE8
- // Enforce case-sensitivity of name attribute
- if ( div.querySelectorAll("[name=d]").length ) {
- rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
- }
-
- // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
- // IE8 throws error here and will not see later tests
- if ( !div.querySelectorAll(":enabled").length ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
-
- // Opera 10-11 does not throw on post-comma invalid pseudos
- div.querySelectorAll("*,:x");
- rbuggyQSA.push(",.*:");
- });
- }
-
- if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
- docElem.webkitMatchesSelector ||
- docElem.mozMatchesSelector ||
- docElem.oMatchesSelector ||
- docElem.msMatchesSelector) )) ) {
-
- assert(function( div ) {
- // Check to see if it's possible to do matchesSelector
- // on a disconnected node (IE 9)
- support.disconnectedMatch = matches.call( div, "div" );
-
- // This should fail with an exception
- // Gecko does not error, returns false instead
- matches.call( div, "[s!='']:x" );
- rbuggyMatches.push( "!=", pseudos );
- });
- }
-
- rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
- rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
-
- /* Contains
- ---------------------------------------------------------------------- */
- hasCompare = rnative.test( docElem.compareDocumentPosition );
-
- // Element contains another
- // Purposefully does not implement inclusive descendent
- // As in, an element does not contain itself
- contains = hasCompare || rnative.test( docElem.contains ) ?
- function( a, b ) {
- var adown = a.nodeType === 9 ? a.documentElement : a,
- bup = b && b.parentNode;
- return a === bup || !!( bup && bup.nodeType === 1 && (
- adown.contains ?
- adown.contains( bup ) :
- a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
- ));
- } :
- function( a, b ) {
- if ( b ) {
- while ( (b = b.parentNode) ) {
- if ( b === a ) {
- return true;
- }
- }
- }
- return false;
- };
-
- /* Sorting
- ---------------------------------------------------------------------- */
-
- // Document order sorting
- sortOrder = hasCompare ?
- function( a, b ) {
-
- // Flag for duplicate removal
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- // Sort on method existence if only one input has compareDocumentPosition
- var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
- if ( compare ) {
- return compare;
- }
-
- // Calculate position if both inputs belong to the same document
- compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
- a.compareDocumentPosition( b ) :
-
- // Otherwise we know they are disconnected
- 1;
-
- // Disconnected nodes
- if ( compare & 1 ||
- (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
-
- // Choose the first element that is related to our preferred document
- if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
- return -1;
- }
- if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
- return 1;
- }
-
- // Maintain original order
- return sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
- 0;
- }
-
- return compare & 4 ? -1 : 1;
- } :
- function( a, b ) {
- // Exit early if the nodes are identical
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- var cur,
- i = 0,
- aup = a.parentNode,
- bup = b.parentNode,
- ap = [ a ],
- bp = [ b ];
-
- // Parentless nodes are either documents or disconnected
- if ( !aup || !bup ) {
- return a === doc ? -1 :
- b === doc ? 1 :
- aup ? -1 :
- bup ? 1 :
- sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
- 0;
-
- // If the nodes are siblings, we can do a quick check
- } else if ( aup === bup ) {
- return siblingCheck( a, b );
- }
-
- // Otherwise we need full lists of their ancestors for comparison
- cur = a;
- while ( (cur = cur.parentNode) ) {
- ap.unshift( cur );
- }
- cur = b;
- while ( (cur = cur.parentNode) ) {
- bp.unshift( cur );
- }
-
- // Walk down the tree looking for a discrepancy
- while ( ap[i] === bp[i] ) {
- i++;
- }
-
- return i ?
- // Do a sibling check if the nodes have a common ancestor
- siblingCheck( ap[i], bp[i] ) :
-
- // Otherwise nodes in our document sort first
- ap[i] === preferredDoc ? -1 :
- bp[i] === preferredDoc ? 1 :
- 0;
- };
-
- return doc;
-};
-
-Sizzle.matches = function( expr, elements ) {
- return Sizzle( expr, null, null, elements );
-};
-
-Sizzle.matchesSelector = function( elem, expr ) {
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
-
- // Make sure that attribute selectors are quoted
- expr = expr.replace( rattributeQuotes, "='$1']" );
-
- if ( support.matchesSelector && documentIsHTML &&
- ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
- ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
-
- try {
- var ret = matches.call( elem, expr );
-
- // IE 9's matchesSelector returns false on disconnected nodes
- if ( ret || support.disconnectedMatch ||
- // As well, disconnected nodes are said to be in a document
- // fragment in IE 9
- elem.document && elem.document.nodeType !== 11 ) {
- return ret;
- }
- } catch(e) {}
- }
-
- return Sizzle( expr, document, null, [ elem ] ).length > 0;
-};
-
-Sizzle.contains = function( context, elem ) {
- // Set document vars if needed
- if ( ( context.ownerDocument || context ) !== document ) {
- setDocument( context );
- }
- return contains( context, elem );
-};
-
-Sizzle.attr = function( elem, name ) {
- // Set document vars if needed
- if ( ( elem.ownerDocument || elem ) !== document ) {
- setDocument( elem );
- }
-
- var fn = Expr.attrHandle[ name.toLowerCase() ],
- // Don't get fooled by Object.prototype properties (jQuery #13807)
- val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
- fn( elem, name, !documentIsHTML ) :
- undefined;
-
- return val !== undefined ?
- val :
- support.attributes || !documentIsHTML ?
- elem.getAttribute( name ) :
- (val = elem.getAttributeNode(name)) && val.specified ?
- val.value :
- null;
-};
-
-Sizzle.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-/**
- * Document sorting and removing duplicates
- * @param {ArrayLike} results
- */
-Sizzle.uniqueSort = function( results ) {
- var elem,
- duplicates = [],
- j = 0,
- i = 0;
-
- // Unless we *know* we can detect duplicates, assume their presence
- hasDuplicate = !support.detectDuplicates;
- sortInput = !support.sortStable && results.slice( 0 );
- results.sort( sortOrder );
-
- if ( hasDuplicate ) {
- while ( (elem = results[i++]) ) {
- if ( elem === results[ i ] ) {
- j = duplicates.push( i );
- }
- }
- while ( j-- ) {
- results.splice( duplicates[ j ], 1 );
- }
- }
-
- // Clear input after sorting to release objects
- // See https://github.com/jquery/sizzle/pull/225
- sortInput = null;
-
- return results;
-};
-
-/**
- * Utility function for retrieving the text value of an array of DOM nodes
- * @param {Array|Element} elem
- */
-getText = Sizzle.getText = function( elem ) {
- var node,
- ret = "",
- i = 0,
- nodeType = elem.nodeType;
-
- if ( !nodeType ) {
- // If no nodeType, this is expected to be an array
- while ( (node = elem[i++]) ) {
- // Do not traverse comment nodes
- ret += getText( node );
- }
- } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
- // Use textContent for elements
- // innerText usage removed for consistency of new lines (jQuery #11153)
- if ( typeof elem.textContent === "string" ) {
- return elem.textContent;
- } else {
- // Traverse its children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- ret += getText( elem );
- }
- }
- } else if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
- // Do not include comment or processing instruction nodes
-
- return ret;
-};
-
-Expr = Sizzle.selectors = {
-
- // Can be adjusted by the user
- cacheLength: 50,
-
- createPseudo: markFunction,
-
- match: matchExpr,
-
- attrHandle: {},
-
- find: {},
-
- relative: {
- ">": { dir: "parentNode", first: true },
- " ": { dir: "parentNode" },
- "+": { dir: "previousSibling", first: true },
- "~": { dir: "previousSibling" }
- },
-
- preFilter: {
- "ATTR": function( match ) {
- match[1] = match[1].replace( runescape, funescape );
-
- // Move the given value to match[3] whether quoted or unquoted
- match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
-
- if ( match[2] === "~=" ) {
- match[3] = " " + match[3] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- "CHILD": function( match ) {
- /* matches from matchExpr["CHILD"]
- 1 type (only|nth|...)
- 2 what (child|of-type)
- 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
- 4 xn-component of xn+y argument ([+-]?\d*n|)
- 5 sign of xn-component
- 6 x of xn-component
- 7 sign of y-component
- 8 y of y-component
- */
- match[1] = match[1].toLowerCase();
-
- if ( match[1].slice( 0, 3 ) === "nth" ) {
- // nth-* requires argument
- if ( !match[3] ) {
- Sizzle.error( match[0] );
- }
-
- // numeric x and y parameters for Expr.filter.CHILD
- // remember that false/true cast respectively to 0/1
- match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
- match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
-
- // other types prohibit arguments
- } else if ( match[3] ) {
- Sizzle.error( match[0] );
- }
-
- return match;
- },
-
- "PSEUDO": function( match ) {
- var excess,
- unquoted = !match[6] && match[2];
-
- if ( matchExpr["CHILD"].test( match[0] ) ) {
- return null;
- }
-
- // Accept quoted arguments as-is
- if ( match[3] ) {
- match[2] = match[4] || match[5] || "";
-
- // Strip excess characters from unquoted arguments
- } else if ( unquoted && rpseudo.test( unquoted ) &&
- // Get excess from tokenize (recursively)
- (excess = tokenize( unquoted, true )) &&
- // advance to the next closing parenthesis
- (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
-
- // excess is a negative index
- match[0] = match[0].slice( 0, excess );
- match[2] = unquoted.slice( 0, excess );
- }
-
- // Return only captures needed by the pseudo filter method (type and argument)
- return match.slice( 0, 3 );
- }
- },
-
- filter: {
-
- "TAG": function( nodeNameSelector ) {
- var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
- return nodeNameSelector === "*" ?
- function() { return true; } :
- function( elem ) {
- return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
- };
- },
-
- "CLASS": function( className ) {
- var pattern = classCache[ className + " " ];
-
- return pattern ||
- (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
- classCache( className, function( elem ) {
- return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
- });
- },
-
- "ATTR": function( name, operator, check ) {
- return function( elem ) {
- var result = Sizzle.attr( elem, name );
-
- if ( result == null ) {
- return operator === "!=";
- }
- if ( !operator ) {
- return true;
- }
-
- result += "";
-
- return operator === "=" ? result === check :
- operator === "!=" ? result !== check :
- operator === "^=" ? check && result.indexOf( check ) === 0 :
- operator === "*=" ? check && result.indexOf( check ) > -1 :
- operator === "$=" ? check && result.slice( -check.length ) === check :
- operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
- operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
- false;
- };
- },
-
- "CHILD": function( type, what, argument, first, last ) {
- var simple = type.slice( 0, 3 ) !== "nth",
- forward = type.slice( -4 ) !== "last",
- ofType = what === "of-type";
-
- return first === 1 && last === 0 ?
-
- // Shortcut for :nth-*(n)
- function( elem ) {
- return !!elem.parentNode;
- } :
-
- function( elem, context, xml ) {
- var cache, outerCache, node, diff, nodeIndex, start,
- dir = simple !== forward ? "nextSibling" : "previousSibling",
- parent = elem.parentNode,
- name = ofType && elem.nodeName.toLowerCase(),
- useCache = !xml && !ofType;
-
- if ( parent ) {
-
- // :(first|last|only)-(child|of-type)
- if ( simple ) {
- while ( dir ) {
- node = elem;
- while ( (node = node[ dir ]) ) {
- if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
- return false;
- }
- }
- // Reverse direction for :only-* (if we haven't yet done so)
- start = dir = type === "only" && !start && "nextSibling";
- }
- return true;
- }
-
- start = [ forward ? parent.firstChild : parent.lastChild ];
-
- // non-xml :nth-child(...) stores cache data on `parent`
- if ( forward && useCache ) {
- // Seek `elem` from a previously-cached index
- outerCache = parent[ expando ] || (parent[ expando ] = {});
- cache = outerCache[ type ] || [];
- nodeIndex = cache[0] === dirruns && cache[1];
- diff = cache[0] === dirruns && cache[2];
- node = nodeIndex && parent.childNodes[ nodeIndex ];
-
- while ( (node = ++nodeIndex && node && node[ dir ] ||
-
- // Fallback to seeking `elem` from the start
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- // When found, cache indexes on `parent` and break
- if ( node.nodeType === 1 && ++diff && node === elem ) {
- outerCache[ type ] = [ dirruns, nodeIndex, diff ];
- break;
- }
- }
-
- // Use previously-cached element index if available
- } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
- diff = cache[1];
-
- // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
- } else {
- // Use the same loop as above to seek `elem` from the start
- while ( (node = ++nodeIndex && node && node[ dir ] ||
- (diff = nodeIndex = 0) || start.pop()) ) {
-
- if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
- // Cache the index of each encountered element
- if ( useCache ) {
- (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
- }
-
- if ( node === elem ) {
- break;
- }
- }
- }
- }
-
- // Incorporate the offset, then check against cycle size
- diff -= last;
- return diff === first || ( diff % first === 0 && diff / first >= 0 );
- }
- };
- },
-
- "PSEUDO": function( pseudo, argument ) {
- // pseudo-class names are case-insensitive
- // http://www.w3.org/TR/selectors/#pseudo-classes
- // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
- // Remember that setFilters inherits from pseudos
- var args,
- fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
- Sizzle.error( "unsupported pseudo: " + pseudo );
-
- // The user may use createPseudo to indicate that
- // arguments are needed to create the filter function
- // just as Sizzle does
- if ( fn[ expando ] ) {
- return fn( argument );
- }
-
- // But maintain support for old signatures
- if ( fn.length > 1 ) {
- args = [ pseudo, pseudo, "", argument ];
- return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
- markFunction(function( seed, matches ) {
- var idx,
- matched = fn( seed, argument ),
- i = matched.length;
- while ( i-- ) {
- idx = indexOf.call( seed, matched[i] );
- seed[ idx ] = !( matches[ idx ] = matched[i] );
- }
- }) :
- function( elem ) {
- return fn( elem, 0, args );
- };
- }
-
- return fn;
- }
- },
-
- pseudos: {
- // Potentially complex pseudos
- "not": markFunction(function( selector ) {
- // Trim the selector passed to compile
- // to avoid treating leading and trailing
- // spaces as combinators
- var input = [],
- results = [],
- matcher = compile( selector.replace( rtrim, "$1" ) );
-
- return matcher[ expando ] ?
- markFunction(function( seed, matches, context, xml ) {
- var elem,
- unmatched = matcher( seed, null, xml, [] ),
- i = seed.length;
-
- // Match elements unmatched by `matcher`
- while ( i-- ) {
- if ( (elem = unmatched[i]) ) {
- seed[i] = !(matches[i] = elem);
- }
- }
- }) :
- function( elem, context, xml ) {
- input[0] = elem;
- matcher( input, null, xml, results );
- return !results.pop();
- };
- }),
-
- "has": markFunction(function( selector ) {
- return function( elem ) {
- return Sizzle( selector, elem ).length > 0;
- };
- }),
-
- "contains": markFunction(function( text ) {
- return function( elem ) {
- return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
- };
- }),
-
- // "Whether an element is represented by a :lang() selector
- // is based solely on the element's language value
- // being equal to the identifier C,
- // or beginning with the identifier C immediately followed by "-".
- // The matching of C against the element's language value is performed case-insensitively.
- // The identifier C does not have to be a valid language name."
- // http://www.w3.org/TR/selectors/#lang-pseudo
- "lang": markFunction( function( lang ) {
- // lang value must be a valid identifier
- if ( !ridentifier.test(lang || "") ) {
- Sizzle.error( "unsupported lang: " + lang );
- }
- lang = lang.replace( runescape, funescape ).toLowerCase();
- return function( elem ) {
- var elemLang;
- do {
- if ( (elemLang = documentIsHTML ?
- elem.lang :
- elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
-
- elemLang = elemLang.toLowerCase();
- return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
- }
- } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
- return false;
- };
- }),
-
- // Miscellaneous
- "target": function( elem ) {
- var hash = window.location && window.location.hash;
- return hash && hash.slice( 1 ) === elem.id;
- },
-
- "root": function( elem ) {
- return elem === docElem;
- },
-
- "focus": function( elem ) {
- return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
- },
-
- // Boolean properties
- "enabled": function( elem ) {
- return elem.disabled === false;
- },
-
- "disabled": function( elem ) {
- return elem.disabled === true;
- },
-
- "checked": function( elem ) {
- // In CSS3, :checked should return both checked and selected elements
- // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
- var nodeName = elem.nodeName.toLowerCase();
- return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
- },
-
- "selected": function( elem ) {
- // Accessing this property makes selected-by-default
- // options in Safari work properly
- if ( elem.parentNode ) {
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- // Contents
- "empty": function( elem ) {
- // http://www.w3.org/TR/selectors/#empty-pseudo
- // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
- // but not by others (comment: 8; processing instruction: 7; etc.)
- // nodeType < 6 works because attributes (2) do not appear as children
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- if ( elem.nodeType < 6 ) {
- return false;
- }
- }
- return true;
- },
-
- "parent": function( elem ) {
- return !Expr.pseudos["empty"]( elem );
- },
-
- // Element/input types
- "header": function( elem ) {
- return rheader.test( elem.nodeName );
- },
-
- "input": function( elem ) {
- return rinputs.test( elem.nodeName );
- },
-
- "button": function( elem ) {
- var name = elem.nodeName.toLowerCase();
- return name === "input" && elem.type === "button" || name === "button";
- },
-
- "text": function( elem ) {
- var attr;
- return elem.nodeName.toLowerCase() === "input" &&
- elem.type === "text" &&
-
- // Support: IE<8
- // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
- ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
- },
-
- // Position-in-collection
- "first": createPositionalPseudo(function() {
- return [ 0 ];
- }),
-
- "last": createPositionalPseudo(function( matchIndexes, length ) {
- return [ length - 1 ];
- }),
-
- "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
- return [ argument < 0 ? argument + length : argument ];
- }),
-
- "even": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 0;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "odd": createPositionalPseudo(function( matchIndexes, length ) {
- var i = 1;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; --i >= 0; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- }),
-
- "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; ++i < length; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- })
- }
-};
-
-Expr.pseudos["nth"] = Expr.pseudos["eq"];
-
-// Add button/input type pseudos
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
- Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
- Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-// Easy API for creating new setFilters
-function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ selector + " " ];
-
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
-
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
-
- while ( soFar ) {
-
- // Comma and first run
- if ( !matched || (match = rcomma.exec( soFar )) ) {
- if ( match ) {
- // Don't consume trailing commas as valid
- soFar = soFar.slice( match[0].length ) || soFar;
- }
- groups.push( (tokens = []) );
- }
-
- matched = false;
-
- // Combinators
- if ( (match = rcombinators.exec( soFar )) ) {
- matched = match.shift();
- tokens.push({
- value: matched,
- // Cast descendant combinators to space
- type: match[0].replace( rtrim, " " )
- });
- soFar = soFar.slice( matched.length );
- }
-
- // Filters
- for ( type in Expr.filter ) {
- if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
- (match = preFilters[ type ]( match ))) ) {
- matched = match.shift();
- tokens.push({
- value: matched,
- type: type,
- matches: match
- });
- soFar = soFar.slice( matched.length );
- }
- }
-
- if ( !matched ) {
- break;
- }
- }
-
- // Return the length of the invalid excess
- // if we're just parsing
- // Otherwise, throw an error or return tokens
- return parseOnly ?
- soFar.length :
- soFar ?
- Sizzle.error( selector ) :
- // Cache the tokens
- tokenCache( selector, groups ).slice( 0 );
-};
-
-function toSelector( tokens ) {
- var i = 0,
- len = tokens.length,
- selector = "";
- for ( ; i < len; i++ ) {
- selector += tokens[i].value;
- }
- return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
- var dir = combinator.dir,
- checkNonElements = base && dir === "parentNode",
- doneName = done++;
-
- return combinator.first ?
- // Check against closest ancestor/preceding element
- function( elem, context, xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- return matcher( elem, context, xml );
- }
- }
- } :
-
- // Check against all ancestor/preceding elements
- function( elem, context, xml ) {
- var oldCache, outerCache,
- newCache = [ dirruns, doneName ];
-
- // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
- if ( xml ) {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- if ( matcher( elem, context, xml ) ) {
- return true;
- }
- }
- }
- } else {
- while ( (elem = elem[ dir ]) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- outerCache = elem[ expando ] || (elem[ expando ] = {});
- if ( (oldCache = outerCache[ dir ]) &&
- oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
-
- // Assign to newCache so results back-propagate to previous elements
- return (newCache[ 2 ] = oldCache[ 2 ]);
- } else {
- // Reuse newcache so results back-propagate to previous elements
- outerCache[ dir ] = newCache;
-
- // A match means we're done; a fail means we have to keep checking
- if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
- return true;
- }
- }
- }
- }
- }
- };
-}
-
-function elementMatcher( matchers ) {
- return matchers.length > 1 ?
- function( elem, context, xml ) {
- var i = matchers.length;
- while ( i-- ) {
- if ( !matchers[i]( elem, context, xml ) ) {
- return false;
- }
- }
- return true;
- } :
- matchers[0];
-}
-
-function multipleContexts( selector, contexts, results ) {
- var i = 0,
- len = contexts.length;
- for ( ; i < len; i++ ) {
- Sizzle( selector, contexts[i], results );
- }
- return results;
-}
-
-function condense( unmatched, map, filter, context, xml ) {
- var elem,
- newUnmatched = [],
- i = 0,
- len = unmatched.length,
- mapped = map != null;
-
- for ( ; i < len; i++ ) {
- if ( (elem = unmatched[i]) ) {
- if ( !filter || filter( elem, context, xml ) ) {
- newUnmatched.push( elem );
- if ( mapped ) {
- map.push( i );
- }
- }
- }
- }
-
- return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
- postFilter = setMatcher( postFilter );
- }
- if ( postFinder && !postFinder[ expando ] ) {
- postFinder = setMatcher( postFinder, postSelector );
- }
- return markFunction(function( seed, results, context, xml ) {
- var temp, i, elem,
- preMap = [],
- postMap = [],
- preexisting = results.length,
-
- // Get initial elements from seed or context
- elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
-
- // Prefilter to get matcher input, preserving a map for seed-results synchronization
- matcherIn = preFilter && ( seed || !selector ) ?
- condense( elems, preMap, preFilter, context, xml ) :
- elems,
-
- matcherOut = matcher ?
- // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
- postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
- // ...intermediate processing is necessary
- [] :
-
- // ...otherwise use results directly
- results :
- matcherIn;
-
- // Find primary matches
- if ( matcher ) {
- matcher( matcherIn, matcherOut, context, xml );
- }
-
- // Apply postFilter
- if ( postFilter ) {
- temp = condense( matcherOut, postMap );
- postFilter( temp, [], context, xml );
-
- // Un-match failing elements by moving them back to matcherIn
- i = temp.length;
- while ( i-- ) {
- if ( (elem = temp[i]) ) {
- matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
- }
- }
- }
-
- if ( seed ) {
- if ( postFinder || preFilter ) {
- if ( postFinder ) {
- // Get the final matcherOut by condensing this intermediate into postFinder contexts
- temp = [];
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) ) {
- // Restore matcherIn since elem is not yet a final match
- temp.push( (matcherIn[i] = elem) );
- }
- }
- postFinder( null, (matcherOut = []), temp, xml );
- }
-
- // Move matched elements from seed to results to keep them synchronized
- i = matcherOut.length;
- while ( i-- ) {
- if ( (elem = matcherOut[i]) &&
- (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
-
- seed[temp] = !(results[temp] = elem);
- }
- }
- }
-
- // Add elements to results, through postFinder if defined
- } else {
- matcherOut = condense(
- matcherOut === results ?
- matcherOut.splice( preexisting, matcherOut.length ) :
- matcherOut
- );
- if ( postFinder ) {
- postFinder( null, results, matcherOut, xml );
- } else {
- push.apply( results, matcherOut );
- }
- }
- });
-}
-
-function matcherFromTokens( tokens ) {
- var checkContext, matcher, j,
- len = tokens.length,
- leadingRelative = Expr.relative[ tokens[0].type ],
- implicitRelative = leadingRelative || Expr.relative[" "],
- i = leadingRelative ? 1 : 0,
-
- // The foundational matcher ensures that elements are reachable from top-level context(s)
- matchContext = addCombinator( function( elem ) {
- return elem === checkContext;
- }, implicitRelative, true ),
- matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
- }, implicitRelative, true ),
- matchers = [ function( elem, context, xml ) {
- return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
- (checkContext = context).nodeType ?
- matchContext( elem, context, xml ) :
- matchAnyContext( elem, context, xml ) );
- } ];
-
- for ( ; i < len; i++ ) {
- if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
- matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
- } else {
- matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
-
- // Return special upon seeing a positional matcher
- if ( matcher[ expando ] ) {
- // Find the next relative operator (if any) for proper handling
- j = ++i;
- for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[j].type ] ) {
- break;
- }
- }
- return setMatcher(
- i > 1 && elementMatcher( matchers ),
- i > 1 && toSelector(
- // If the preceding token was a descendant combinator, insert an implicit any-element `*`
- tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
- ).replace( rtrim, "$1" ),
- matcher,
- i < j && matcherFromTokens( tokens.slice( i, j ) ),
- j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
- j < len && toSelector( tokens )
- );
- }
- matchers.push( matcher );
- }
- }
-
- return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
- var bySet = setMatchers.length > 0,
- byElement = elementMatchers.length > 0,
- superMatcher = function( seed, context, xml, results, outermost ) {
- var elem, j, matcher,
- matchedCount = 0,
- i = "0",
- unmatched = seed && [],
- setMatched = [],
- contextBackup = outermostContext,
- // We must always have either seed elements or outermost context
- elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
- // Use integer dirruns iff this is the outermost matcher
- dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
- len = elems.length;
-
- if ( outermost ) {
- outermostContext = context !== document && context;
- }
-
- // Add elements passing elementMatchers directly to results
- // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
- // Support: IE<9, Safari
- // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id
- for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
- if ( byElement && elem ) {
- j = 0;
- while ( (matcher = elementMatchers[j++]) ) {
- if ( matcher( elem, context, xml ) ) {
- results.push( elem );
- break;
- }
- }
- if ( outermost ) {
- dirruns = dirrunsUnique;
- }
- }
-
- // Track unmatched elements for set filters
- if ( bySet ) {
- // They will have gone through all possible matchers
- if ( (elem = !matcher && elem) ) {
- matchedCount--;
- }
-
- // Lengthen the array for every element, matched or not
- if ( seed ) {
- unmatched.push( elem );
- }
- }
- }
-
- // Apply set filters to unmatched elements
- matchedCount += i;
- if ( bySet && i !== matchedCount ) {
- j = 0;
- while ( (matcher = setMatchers[j++]) ) {
- matcher( unmatched, setMatched, context, xml );
- }
-
- if ( seed ) {
- // Reintegrate element matches to eliminate the need for sorting
- if ( matchedCount > 0 ) {
- while ( i-- ) {
- if ( !(unmatched[i] || setMatched[i]) ) {
- setMatched[i] = pop.call( results );
- }
- }
- }
-
- // Discard index placeholder values to get only actual matches
- setMatched = condense( setMatched );
- }
-
- // Add matches to results
- push.apply( results, setMatched );
-
- // Seedless set matches succeeding multiple successful matchers stipulate sorting
- if ( outermost && !seed && setMatched.length > 0 &&
- ( matchedCount + setMatchers.length ) > 1 ) {
-
- Sizzle.uniqueSort( results );
- }
- }
-
- // Override manipulation of globals by nested matchers
- if ( outermost ) {
- dirruns = dirrunsUnique;
- outermostContext = contextBackup;
- }
-
- return unmatched;
- };
-
- return bySet ?
- markFunction( superMatcher ) :
- superMatcher;
-}
-
-compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
- var i,
- setMatchers = [],
- elementMatchers = [],
- cached = compilerCache[ selector + " " ];
-
- if ( !cached ) {
- // Generate a function of recursive functions that can be used to check each element
- if ( !match ) {
- match = tokenize( selector );
- }
- i = match.length;
- while ( i-- ) {
- cached = matcherFromTokens( match[i] );
- if ( cached[ expando ] ) {
- setMatchers.push( cached );
- } else {
- elementMatchers.push( cached );
- }
- }
-
- // Cache the compiled function
- cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
-
- // Save selector and tokenization
- cached.selector = selector;
- }
- return cached;
-};
-
-/**
- * A low-level selection function that works with Sizzle's compiled
- * selector functions
- * @param {String|Function} selector A selector or a pre-compiled
- * selector function built with Sizzle.compile
- * @param {Element} context
- * @param {Array} [results]
- * @param {Array} [seed] A set of elements to match against
- */
-select = Sizzle.select = function( selector, context, results, seed ) {
- var i, tokens, token, type, find,
- compiled = typeof selector === "function" && selector,
- match = !seed && tokenize( (selector = compiled.selector || selector) );
-
- results = results || [];
-
- // Try to minimize operations if there is no seed and only one group
- if ( match.length === 1 ) {
-
- // Take a shortcut and set the context if the root selector is an ID
- tokens = match[0] = match[0].slice( 0 );
- if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
- support.getById && context.nodeType === 9 && documentIsHTML &&
- Expr.relative[ tokens[1].type ] ) {
-
- context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
- if ( !context ) {
- return results;
-
- // Precompiled matchers will still verify ancestry, so step up a level
- } else if ( compiled ) {
- context = context.parentNode;
- }
-
- selector = selector.slice( tokens.shift().value.length );
- }
-
- // Fetch a seed set for right-to-left matching
- i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
- while ( i-- ) {
- token = tokens[i];
-
- // Abort if we hit a combinator
- if ( Expr.relative[ (type = token.type) ] ) {
- break;
- }
- if ( (find = Expr.find[ type ]) ) {
- // Search, expanding context for leading sibling combinators
- if ( (seed = find(
- token.matches[0].replace( runescape, funescape ),
- rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
- )) ) {
-
- // If seed is empty or no tokens remain, we can return early
- tokens.splice( i, 1 );
- selector = seed.length && toSelector( tokens );
- if ( !selector ) {
- push.apply( results, seed );
- return results;
- }
-
- break;
- }
- }
- }
- }
-
- // Compile and execute a filtering function if one is not provided
- // Provide `match` to avoid retokenization if we modified the selector above
- ( compiled || compile( selector, match ) )(
- seed,
- context,
- !documentIsHTML,
- results,
- rsibling.test( selector ) && testContext( context.parentNode ) || context
- );
- return results;
-};
-
-// One-time assignments
-
-// Sort stability
-support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
-
-// Support: Chrome<14
-// Always assume duplicates if they aren't passed to the comparison function
-support.detectDuplicates = !!hasDuplicate;
-
-// Initialize against the default document
-setDocument();
-
-// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
-// Detached nodes confoundingly follow *each other*
-support.sortDetached = assert(function( div1 ) {
- // Should return 1, but returns 4 (following)
- return div1.compareDocumentPosition( document.createElement("div") ) & 1;
-});
-
-// Support: IE<8
-// Prevent attribute/property "interpolation"
-// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
-if ( !assert(function( div ) {
- div.innerHTML = "";
- return div.firstChild.getAttribute("href") === "#" ;
-}) ) {
- addHandle( "type|href|height|width", function( elem, name, isXML ) {
- if ( !isXML ) {
- return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
- }
- });
-}
-
-// Support: IE<9
-// Use defaultValue in place of getAttribute("value")
-if ( !support.attributes || !assert(function( div ) {
- div.innerHTML = "";
- div.firstChild.setAttribute( "value", "" );
- return div.firstChild.getAttribute( "value" ) === "";
-}) ) {
- addHandle( "value", function( elem, name, isXML ) {
- if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
- return elem.defaultValue;
- }
- });
-}
-
-// Support: IE<9
-// Use getAttributeNode to fetch booleans when getAttribute lies
-if ( !assert(function( div ) {
- return div.getAttribute("disabled") == null;
-}) ) {
- addHandle( booleans, function( elem, name, isXML ) {
- var val;
- if ( !isXML ) {
- return elem[ name ] === true ? name.toLowerCase() :
- (val = elem.getAttributeNode( name )) && val.specified ?
- val.value :
- null;
- }
- });
-}
-
-return Sizzle;
-
-})( window );
-
-
-
-jQuery.find = Sizzle;
-jQuery.expr = Sizzle.selectors;
-jQuery.expr[":"] = jQuery.expr.pseudos;
-jQuery.unique = Sizzle.uniqueSort;
-jQuery.text = Sizzle.getText;
-jQuery.isXMLDoc = Sizzle.isXML;
-jQuery.contains = Sizzle.contains;
-
-
-
-var rneedsContext = jQuery.expr.match.needsContext;
-
-var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
-
-
-
-var risSimple = /^.[^:#\[\.,]*$/;
-
-// Implement the identical functionality for filter and not
-function winnow( elements, qualifier, not ) {
- if ( jQuery.isFunction( qualifier ) ) {
- return jQuery.grep( elements, function( elem, i ) {
- /* jshint -W018 */
- return !!qualifier.call( elem, i, elem ) !== not;
- });
-
- }
-
- if ( qualifier.nodeType ) {
- return jQuery.grep( elements, function( elem ) {
- return ( elem === qualifier ) !== not;
- });
-
- }
-
- if ( typeof qualifier === "string" ) {
- if ( risSimple.test( qualifier ) ) {
- return jQuery.filter( qualifier, elements, not );
- }
-
- qualifier = jQuery.filter( qualifier, elements );
- }
-
- return jQuery.grep( elements, function( elem ) {
- return ( jQuery.inArray( elem, qualifier ) >= 0 ) !== not;
- });
-}
-
-jQuery.filter = function( expr, elems, not ) {
- var elem = elems[ 0 ];
-
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- return elems.length === 1 && elem.nodeType === 1 ?
- jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
- jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
- return elem.nodeType === 1;
- }));
-};
-
-jQuery.fn.extend({
- find: function( selector ) {
- var i,
- ret = [],
- self = this,
- len = self.length;
-
- if ( typeof selector !== "string" ) {
- return this.pushStack( jQuery( selector ).filter(function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- }) );
- }
-
- for ( i = 0; i < len; i++ ) {
- jQuery.find( selector, self[ i ], ret );
- }
-
- // Needed because $( selector, context ) becomes $( context ).find( selector )
- ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
- ret.selector = this.selector ? this.selector + " " + selector : selector;
- return ret;
- },
- filter: function( selector ) {
- return this.pushStack( winnow(this, selector || [], false) );
- },
- not: function( selector ) {
- return this.pushStack( winnow(this, selector || [], true) );
- },
- is: function( selector ) {
- return !!winnow(
- this,
-
- // If this is a positional/relative selector, check membership in the returned set
- // so $("p:first").is("p:last") won't return true for a doc with two "p".
- typeof selector === "string" && rneedsContext.test( selector ) ?
- jQuery( selector ) :
- selector || [],
- false
- ).length;
- }
-});
-
-
-// Initialize a jQuery object
-
-
-// A central reference to the root jQuery(document)
-var rootjQuery,
-
- // Use the correct document accordingly with window argument (sandbox)
- document = window.document,
-
- // A simple way to check for HTML strings
- // Prioritize #id over to avoid XSS via location.hash (#9521)
- // Strict HTML recognition (#11290: must start with <)
- rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
-
- init = jQuery.fn.init = function( selector, context ) {
- var match, elem;
-
- // HANDLE: $(""), $(null), $(undefined), $(false)
- if ( !selector ) {
- return this;
- }
-
- // Handle HTML strings
- if ( typeof selector === "string" ) {
- if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
- // Assume that strings that start and end with <> are HTML and skip the regex check
- match = [ null, selector, null ];
-
- } else {
- match = rquickExpr.exec( selector );
- }
-
- // Match html or make sure no context is specified for #id
- if ( match && (match[1] || !context) ) {
-
- // HANDLE: $(html) -> $(array)
- if ( match[1] ) {
- context = context instanceof jQuery ? context[0] : context;
-
- // scripts is true for back-compat
- // Intentionally let the error be thrown if parseHTML is not present
- jQuery.merge( this, jQuery.parseHTML(
- match[1],
- context && context.nodeType ? context.ownerDocument || context : document,
- true
- ) );
-
- // HANDLE: $(html, props)
- if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
- for ( match in context ) {
- // Properties of context are called as methods if possible
- if ( jQuery.isFunction( this[ match ] ) ) {
- this[ match ]( context[ match ] );
-
- // ...and otherwise set as attributes
- } else {
- this.attr( match, context[ match ] );
- }
- }
- }
-
- return this;
-
- // HANDLE: $(#id)
- } else {
- elem = document.getElementById( match[2] );
-
- // Check parentNode to catch when Blackberry 4.6 returns
- // nodes that are no longer in the document #6963
- if ( elem && elem.parentNode ) {
- // Handle the case where IE and Opera return items
- // by name instead of ID
- if ( elem.id !== match[2] ) {
- return rootjQuery.find( selector );
- }
-
- // Otherwise, we inject the element directly into the jQuery object
- this.length = 1;
- this[0] = elem;
- }
-
- this.context = document;
- this.selector = selector;
- return this;
- }
-
- // HANDLE: $(expr, $(...))
- } else if ( !context || context.jquery ) {
- return ( context || rootjQuery ).find( selector );
-
- // HANDLE: $(expr, context)
- // (which is just equivalent to: $(context).find(expr)
- } else {
- return this.constructor( context ).find( selector );
- }
-
- // HANDLE: $(DOMElement)
- } else if ( selector.nodeType ) {
- this.context = this[0] = selector;
- this.length = 1;
- return this;
-
- // HANDLE: $(function)
- // Shortcut for document ready
- } else if ( jQuery.isFunction( selector ) ) {
- return typeof rootjQuery.ready !== "undefined" ?
- rootjQuery.ready( selector ) :
- // Execute immediately if ready is not present
- selector( jQuery );
- }
-
- if ( selector.selector !== undefined ) {
- this.selector = selector.selector;
- this.context = selector.context;
- }
-
- return jQuery.makeArray( selector, this );
- };
-
-// Give the init function the jQuery prototype for later instantiation
-init.prototype = jQuery.fn;
-
-// Initialize central reference
-rootjQuery = jQuery( document );
-
-
-var rparentsprev = /^(?:parents|prev(?:Until|All))/,
- // methods guaranteed to produce a unique set when starting from a unique set
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.extend({
- dir: function( elem, dir, until ) {
- var matched = [],
- cur = elem[ dir ];
-
- while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
- if ( cur.nodeType === 1 ) {
- matched.push( cur );
- }
- cur = cur[dir];
- }
- return matched;
- },
-
- sibling: function( n, elem ) {
- var r = [];
-
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType === 1 && n !== elem ) {
- r.push( n );
- }
- }
-
- return r;
- }
-});
-
-jQuery.fn.extend({
- has: function( target ) {
- var i,
- targets = jQuery( target, this ),
- len = targets.length;
-
- return this.filter(function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( this, targets[i] ) ) {
- return true;
- }
- }
- });
- },
-
- closest: function( selectors, context ) {
- var cur,
- i = 0,
- l = this.length,
- matched = [],
- pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
-
- for ( ; i < l; i++ ) {
- for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
- // Always skip document fragments
- if ( cur.nodeType < 11 && (pos ?
- pos.index(cur) > -1 :
-
- // Don't pass non-elements to Sizzle
- cur.nodeType === 1 &&
- jQuery.find.matchesSelector(cur, selectors)) ) {
-
- matched.push( cur );
- break;
- }
- }
- }
-
- return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
- },
-
- // Determine the position of an element within
- // the matched set of elements
- index: function( elem ) {
-
- // No argument, return index in parent
- if ( !elem ) {
- return ( this[0] && this[0].parentNode ) ? this.first().prevAll().length : -1;
- }
-
- // index in selector
- if ( typeof elem === "string" ) {
- return jQuery.inArray( this[0], jQuery( elem ) );
- }
-
- // Locate the position of the desired element
- return jQuery.inArray(
- // If it receives a jQuery object, the first element is used
- elem.jquery ? elem[0] : elem, this );
- },
-
- add: function( selector, context ) {
- return this.pushStack(
- jQuery.unique(
- jQuery.merge( this.get(), jQuery( selector, context ) )
- )
- );
- },
-
- addBack: function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter(selector)
- );
- }
-});
-
-function sibling( cur, dir ) {
- do {
- cur = cur[ dir ];
- } while ( cur && cur.nodeType !== 1 );
-
- return cur;
-}
-
-jQuery.each({
- parent: function( elem ) {
- var parent = elem.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
- },
- parents: function( elem ) {
- return jQuery.dir( elem, "parentNode" );
- },
- parentsUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "parentNode", until );
- },
- next: function( elem ) {
- return sibling( elem, "nextSibling" );
- },
- prev: function( elem ) {
- return sibling( elem, "previousSibling" );
- },
- nextAll: function( elem ) {
- return jQuery.dir( elem, "nextSibling" );
- },
- prevAll: function( elem ) {
- return jQuery.dir( elem, "previousSibling" );
- },
- nextUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "nextSibling", until );
- },
- prevUntil: function( elem, i, until ) {
- return jQuery.dir( elem, "previousSibling", until );
- },
- siblings: function( elem ) {
- return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
- },
- children: function( elem ) {
- return jQuery.sibling( elem.firstChild );
- },
- contents: function( elem ) {
- return jQuery.nodeName( elem, "iframe" ) ?
- elem.contentDocument || elem.contentWindow.document :
- jQuery.merge( [], elem.childNodes );
- }
-}, function( name, fn ) {
- jQuery.fn[ name ] = function( until, selector ) {
- var ret = jQuery.map( this, fn, until );
-
- if ( name.slice( -5 ) !== "Until" ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- ret = jQuery.filter( selector, ret );
- }
-
- if ( this.length > 1 ) {
- // Remove duplicates
- if ( !guaranteedUnique[ name ] ) {
- ret = jQuery.unique( ret );
- }
-
- // Reverse order for parents* and prev-derivatives
- if ( rparentsprev.test( name ) ) {
- ret = ret.reverse();
- }
- }
-
- return this.pushStack( ret );
- };
-});
-var rnotwhite = (/\S+/g);
-
-
-
-// String to Object options format cache
-var optionsCache = {};
-
-// Convert String-formatted options into Object-formatted ones and store in cache
-function createOptions( options ) {
- var object = optionsCache[ options ] = {};
- jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
- object[ flag ] = true;
- });
- return object;
-}
-
-/*
- * Create a callback list using the following parameters:
- *
- * options: an optional list of space-separated options that will change how
- * the callback list behaves or a more traditional option object
- *
- * By default a callback list will act like an event callback list and can be
- * "fired" multiple times.
- *
- * Possible options:
- *
- * once: will ensure the callback list can only be fired once (like a Deferred)
- *
- * memory: will keep track of previous values and will call any callback added
- * after the list has been fired right away with the latest "memorized"
- * values (like a Deferred)
- *
- * unique: will ensure a callback can only be added once (no duplicate in the list)
- *
- * stopOnFalse: interrupt callings when a callback returns false
- *
- */
-jQuery.Callbacks = function( options ) {
-
- // Convert options from String-formatted to Object-formatted if needed
- // (we check in cache first)
- options = typeof options === "string" ?
- ( optionsCache[ options ] || createOptions( options ) ) :
- jQuery.extend( {}, options );
-
- var // Flag to know if list is currently firing
- firing,
- // Last fire value (for non-forgettable lists)
- memory,
- // Flag to know if list was already fired
- fired,
- // End of the loop when firing
- firingLength,
- // Index of currently firing callback (modified by remove if needed)
- firingIndex,
- // First callback to fire (used internally by add and fireWith)
- firingStart,
- // Actual callback list
- list = [],
- // Stack of fire calls for repeatable lists
- stack = !options.once && [],
- // Fire callbacks
- fire = function( data ) {
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
- memory = false; // To prevent further calls using add
- break;
- }
- }
- firing = false;
- if ( list ) {
- if ( stack ) {
- if ( stack.length ) {
- fire( stack.shift() );
- }
- } else if ( memory ) {
- list = [];
- } else {
- self.disable();
- }
- }
- },
- // Actual Callbacks object
- self = {
- // Add a callback or a collection of callbacks to the list
- add: function() {
- if ( list ) {
- // First, we save the current length
- var start = list.length;
- (function add( args ) {
- jQuery.each( args, function( _, arg ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
- if ( !options.unique || !self.has( arg ) ) {
- list.push( arg );
- }
- } else if ( arg && arg.length && type !== "string" ) {
- // Inspect recursively
- add( arg );
- }
- });
- })( arguments );
- // Do we need to add the callbacks to the
- // current firing batch?
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- } else if ( memory ) {
- firingStart = start;
- fire( memory );
- }
- }
- return this;
- },
- // Remove a callback from the list
- remove: function() {
- if ( list ) {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
- // Handle firing indexes
- if ( firing ) {
- if ( index <= firingLength ) {
- firingLength--;
- }
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
- }
- });
- }
- return this;
- },
- // Check if a given callback is in the list.
- // If no argument is given, return whether or not list has callbacks attached.
- has: function( fn ) {
- return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
- },
- // Remove all callbacks from the list
- empty: function() {
- list = [];
- firingLength = 0;
- return this;
- },
- // Have the list do nothing anymore
- disable: function() {
- list = stack = memory = undefined;
- return this;
- },
- // Is it disabled?
- disabled: function() {
- return !list;
- },
- // Lock the list in its current state
- lock: function() {
- stack = undefined;
- if ( !memory ) {
- self.disable();
- }
- return this;
- },
- // Is it locked?
- locked: function() {
- return !stack;
- },
- // Call all callbacks with the given context and arguments
- fireWith: function( context, args ) {
- if ( list && ( !fired || stack ) ) {
- args = args || [];
- args = [ context, args.slice ? args.slice() : args ];
- if ( firing ) {
- stack.push( args );
- } else {
- fire( args );
- }
- }
- return this;
- },
- // Call all the callbacks with the given arguments
- fire: function() {
- self.fireWith( this, arguments );
- return this;
- },
- // To know if the callbacks have already been called at least once
- fired: function() {
- return !!fired;
- }
- };
-
- return self;
-};
-
-
-jQuery.extend({
-
- Deferred: function( func ) {
- var tuples = [
- // action, add listener, listener list, final state
- [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
- [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
- [ "notify", "progress", jQuery.Callbacks("memory") ]
- ],
- state = "pending",
- promise = {
- state: function() {
- return state;
- },
- always: function() {
- deferred.done( arguments ).fail( arguments );
- return this;
- },
- then: function( /* fnDone, fnFail, fnProgress */ ) {
- var fns = arguments;
- return jQuery.Deferred(function( newDefer ) {
- jQuery.each( tuples, function( i, tuple ) {
- var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
- // deferred[ done | fail | progress ] for forwarding actions to newDefer
- deferred[ tuple[1] ](function() {
- var returned = fn && fn.apply( this, arguments );
- if ( returned && jQuery.isFunction( returned.promise ) ) {
- returned.promise()
- .done( newDefer.resolve )
- .fail( newDefer.reject )
- .progress( newDefer.notify );
- } else {
- newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
- }
- });
- });
- fns = null;
- }).promise();
- },
- // Get a promise for this deferred
- // If obj is provided, the promise aspect is added to the object
- promise: function( obj ) {
- return obj != null ? jQuery.extend( obj, promise ) : promise;
- }
- },
- deferred = {};
-
- // Keep pipe for back-compat
- promise.pipe = promise.then;
-
- // Add list-specific methods
- jQuery.each( tuples, function( i, tuple ) {
- var list = tuple[ 2 ],
- stateString = tuple[ 3 ];
-
- // promise[ done | fail | progress ] = list.add
- promise[ tuple[1] ] = list.add;
-
- // Handle state
- if ( stateString ) {
- list.add(function() {
- // state = [ resolved | rejected ]
- state = stateString;
-
- // [ reject_list | resolve_list ].disable; progress_list.lock
- }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
- }
-
- // deferred[ resolve | reject | notify ]
- deferred[ tuple[0] ] = function() {
- deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
- return this;
- };
- deferred[ tuple[0] + "With" ] = list.fireWith;
- });
-
- // Make the deferred a promise
- promise.promise( deferred );
-
- // Call given func if any
- if ( func ) {
- func.call( deferred, deferred );
- }
-
- // All done!
- return deferred;
- },
-
- // Deferred helper
- when: function( subordinate /* , ..., subordinateN */ ) {
- var i = 0,
- resolveValues = slice.call( arguments ),
- length = resolveValues.length,
-
- // the count of uncompleted subordinates
- remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
-
- // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
- deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
-
- // Update function for both resolve and progress values
- updateFunc = function( i, contexts, values ) {
- return function( value ) {
- contexts[ i ] = this;
- values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
- if ( values === progressValues ) {
- deferred.notifyWith( contexts, values );
-
- } else if ( !(--remaining) ) {
- deferred.resolveWith( contexts, values );
- }
- };
- },
-
- progressValues, progressContexts, resolveContexts;
-
- // add listeners to Deferred subordinates; treat others as resolved
- if ( length > 1 ) {
- progressValues = new Array( length );
- progressContexts = new Array( length );
- resolveContexts = new Array( length );
- for ( ; i < length; i++ ) {
- if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
- resolveValues[ i ].promise()
- .done( updateFunc( i, resolveContexts, resolveValues ) )
- .fail( deferred.reject )
- .progress( updateFunc( i, progressContexts, progressValues ) );
- } else {
- --remaining;
- }
- }
- }
-
- // if we're not waiting on anything, resolve the master
- if ( !remaining ) {
- deferred.resolveWith( resolveContexts, resolveValues );
- }
-
- return deferred.promise();
- }
-});
-
-
-// The deferred used on DOM ready
-var readyList;
-
-jQuery.fn.ready = function( fn ) {
- // Add the callback
- jQuery.ready.promise().done( fn );
-
- return this;
-};
-
-jQuery.extend({
- // Is the DOM ready to be used? Set to true once it occurs.
- isReady: false,
-
- // A counter to track how many items to wait for before
- // the ready event fires. See #6781
- readyWait: 1,
-
- // Hold (or release) the ready event
- holdReady: function( hold ) {
- if ( hold ) {
- jQuery.readyWait++;
- } else {
- jQuery.ready( true );
- }
- },
-
- // Handle when the DOM is ready
- ready: function( wait ) {
-
- // Abort if there are pending holds or we're already ready
- if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
- return;
- }
-
- // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
- if ( !document.body ) {
- return setTimeout( jQuery.ready );
- }
-
- // Remember that the DOM is ready
- jQuery.isReady = true;
-
- // If a normal DOM Ready event fired, decrement, and wait if need be
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- // If there are functions bound, to execute
- readyList.resolveWith( document, [ jQuery ] );
-
- // Trigger any bound ready events
- if ( jQuery.fn.triggerHandler ) {
- jQuery( document ).triggerHandler( "ready" );
- jQuery( document ).off( "ready" );
- }
- }
-});
-
-/**
- * Clean-up method for dom ready events
- */
-function detach() {
- if ( document.addEventListener ) {
- document.removeEventListener( "DOMContentLoaded", completed, false );
- window.removeEventListener( "load", completed, false );
-
- } else {
- document.detachEvent( "onreadystatechange", completed );
- window.detachEvent( "onload", completed );
- }
-}
-
-/**
- * The ready event handler and self cleanup method
- */
-function completed() {
- // readyState === "complete" is good enough for us to call the dom ready in oldIE
- if ( document.addEventListener || event.type === "load" || document.readyState === "complete" ) {
- detach();
- jQuery.ready();
- }
-}
-
-jQuery.ready.promise = function( obj ) {
- if ( !readyList ) {
-
- readyList = jQuery.Deferred();
-
- // Catch cases where $(document).ready() is called after the browser event has already occurred.
- // we once tried to use readyState "interactive" here, but it caused issues like the one
- // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
- if ( document.readyState === "complete" ) {
- // Handle it asynchronously to allow scripts the opportunity to delay ready
- setTimeout( jQuery.ready );
-
- // Standards-based browsers support DOMContentLoaded
- } else if ( document.addEventListener ) {
- // Use the handy event callback
- document.addEventListener( "DOMContentLoaded", completed, false );
-
- // A fallback to window.onload, that will always work
- window.addEventListener( "load", completed, false );
-
- // If IE event model is used
- } else {
- // Ensure firing before onload, maybe late but safe also for iframes
- document.attachEvent( "onreadystatechange", completed );
-
- // A fallback to window.onload, that will always work
- window.attachEvent( "onload", completed );
-
- // If IE and not a frame
- // continually check to see if the document is ready
- var top = false;
-
- try {
- top = window.frameElement == null && document.documentElement;
- } catch(e) {}
-
- if ( top && top.doScroll ) {
- (function doScrollCheck() {
- if ( !jQuery.isReady ) {
-
- try {
- // Use the trick by Diego Perini
- // http://javascript.nwbox.com/IEContentLoaded/
- top.doScroll("left");
- } catch(e) {
- return setTimeout( doScrollCheck, 50 );
- }
-
- // detach all dom ready events
- detach();
-
- // and execute any waiting functions
- jQuery.ready();
- }
- })();
- }
- }
- }
- return readyList.promise( obj );
-};
-
-
-var strundefined = typeof undefined;
-
-
-
-// Support: IE<9
-// Iteration over object's inherited properties before its own
-var i;
-for ( i in jQuery( support ) ) {
- break;
-}
-support.ownLast = i !== "0";
-
-// Note: most support tests are defined in their respective modules.
-// false until the test is run
-support.inlineBlockNeedsLayout = false;
-
-// Execute ASAP in case we need to set body.style.zoom
-jQuery(function() {
- // Minified: var a,b,c,d
- var val, div, body, container;
-
- body = document.getElementsByTagName( "body" )[ 0 ];
- if ( !body || !body.style ) {
- // Return for frameset docs that don't have a body
- return;
- }
-
- // Setup
- div = document.createElement( "div" );
- container = document.createElement( "div" );
- container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px";
- body.appendChild( container ).appendChild( div );
-
- if ( typeof div.style.zoom !== strundefined ) {
- // Support: IE<8
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1";
-
- support.inlineBlockNeedsLayout = val = div.offsetWidth === 3;
- if ( val ) {
- // Prevent IE 6 from affecting layout for positioned elements #11048
- // Prevent IE from shrinking the body in IE 7 mode #12869
- // Support: IE<8
- body.style.zoom = 1;
- }
- }
-
- body.removeChild( container );
-});
-
-
-
-
-(function() {
- var div = document.createElement( "div" );
-
- // Execute the test only if not already executed in another module.
- if (support.deleteExpando == null) {
- // Support: IE<9
- support.deleteExpando = true;
- try {
- delete div.test;
- } catch( e ) {
- support.deleteExpando = false;
- }
- }
-
- // Null elements to avoid leaks in IE.
- div = null;
-})();
-
-
-/**
- * Determines whether an object can have data
- */
-jQuery.acceptData = function( elem ) {
- var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ],
- nodeType = +elem.nodeType || 1;
-
- // Do not set data on non-element DOM nodes because it will not be cleared (#8335).
- return nodeType !== 1 && nodeType !== 9 ?
- false :
-
- // Nodes accept data unless otherwise specified; rejection can be conditional
- !noData || noData !== true && elem.getAttribute("classid") === noData;
-};
-
-
-var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
- rmultiDash = /([A-Z])/g;
-
-function dataAttr( elem, key, data ) {
- // If nothing was found internally, try to fetch any
- // data from the HTML5 data-* attribute
- if ( data === undefined && elem.nodeType === 1 ) {
-
- var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
-
- data = elem.getAttribute( name );
-
- if ( typeof data === "string" ) {
- try {
- data = data === "true" ? true :
- data === "false" ? false :
- data === "null" ? null :
- // Only convert to a number if it doesn't change the string
- +data + "" === data ? +data :
- rbrace.test( data ) ? jQuery.parseJSON( data ) :
- data;
- } catch( e ) {}
-
- // Make sure we set the data so it isn't changed later
- jQuery.data( elem, key, data );
-
- } else {
- data = undefined;
- }
- }
-
- return data;
-}
-
-// checks a cache object for emptiness
-function isEmptyDataObject( obj ) {
- var name;
- for ( name in obj ) {
-
- // if the public data object is empty, the private is still empty
- if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
- continue;
- }
- if ( name !== "toJSON" ) {
- return false;
- }
- }
-
- return true;
-}
-
-function internalData( elem, name, data, pvt /* Internal Use Only */ ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var ret, thisCache,
- internalKey = jQuery.expando,
-
- // We have to handle DOM nodes and JS objects differently because IE6-7
- // can't GC object references properly across the DOM-JS boundary
- isNode = elem.nodeType,
-
- // Only DOM nodes need the global jQuery cache; JS object data is
- // attached directly to the object so GC can occur automatically
- cache = isNode ? jQuery.cache : elem,
-
- // Only defining an ID for JS objects if its cache already exists allows
- // the code to shortcut on the same path as a DOM node with no cache
- id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
-
- // Avoid doing any more work than we need to when trying to get data on an
- // object that has no data at all
- if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) {
- return;
- }
-
- if ( !id ) {
- // Only DOM nodes need a new unique ID for each element since their data
- // ends up in the global cache
- if ( isNode ) {
- id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++;
- } else {
- id = internalKey;
- }
- }
-
- if ( !cache[ id ] ) {
- // Avoid exposing jQuery metadata on plain JS objects when the object
- // is serialized using JSON.stringify
- cache[ id ] = isNode ? {} : { toJSON: jQuery.noop };
- }
-
- // An object can be passed to jQuery.data instead of a key/value pair; this gets
- // shallow copied over onto the existing cache
- if ( typeof name === "object" || typeof name === "function" ) {
- if ( pvt ) {
- cache[ id ] = jQuery.extend( cache[ id ], name );
- } else {
- cache[ id ].data = jQuery.extend( cache[ id ].data, name );
- }
- }
-
- thisCache = cache[ id ];
-
- // jQuery data() is stored in a separate object inside the object's internal data
- // cache in order to avoid key collisions between internal data and user-defined
- // data.
- if ( !pvt ) {
- if ( !thisCache.data ) {
- thisCache.data = {};
- }
-
- thisCache = thisCache.data;
- }
-
- if ( data !== undefined ) {
- thisCache[ jQuery.camelCase( name ) ] = data;
- }
-
- // Check for both converted-to-camel and non-converted data property names
- // If a data property was specified
- if ( typeof name === "string" ) {
-
- // First Try to find as-is property data
- ret = thisCache[ name ];
-
- // Test for null|undefined property data
- if ( ret == null ) {
-
- // Try to find the camelCased property
- ret = thisCache[ jQuery.camelCase( name ) ];
- }
- } else {
- ret = thisCache;
- }
-
- return ret;
-}
-
-function internalRemoveData( elem, name, pvt ) {
- if ( !jQuery.acceptData( elem ) ) {
- return;
- }
-
- var thisCache, i,
- isNode = elem.nodeType,
-
- // See jQuery.data for more information
- cache = isNode ? jQuery.cache : elem,
- id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
-
- // If there is already no cache entry for this object, there is no
- // purpose in continuing
- if ( !cache[ id ] ) {
- return;
- }
-
- if ( name ) {
-
- thisCache = pvt ? cache[ id ] : cache[ id ].data;
-
- if ( thisCache ) {
-
- // Support array or space separated string names for data keys
- if ( !jQuery.isArray( name ) ) {
-
- // try the string as a key before any manipulation
- if ( name in thisCache ) {
- name = [ name ];
- } else {
-
- // split the camel cased version by spaces unless a key with the spaces exists
- name = jQuery.camelCase( name );
- if ( name in thisCache ) {
- name = [ name ];
- } else {
- name = name.split(" ");
- }
- }
- } else {
- // If "name" is an array of keys...
- // When data is initially created, via ("key", "val") signature,
- // keys will be converted to camelCase.
- // Since there is no way to tell _how_ a key was added, remove
- // both plain key and camelCase key. #12786
- // This will only penalize the array argument path.
- name = name.concat( jQuery.map( name, jQuery.camelCase ) );
- }
-
- i = name.length;
- while ( i-- ) {
- delete thisCache[ name[i] ];
- }
-
- // If there is no data left in the cache, we want to continue
- // and let the cache object itself get destroyed
- if ( pvt ? !isEmptyDataObject(thisCache) : !jQuery.isEmptyObject(thisCache) ) {
- return;
- }
- }
- }
-
- // See jQuery.data for more information
- if ( !pvt ) {
- delete cache[ id ].data;
-
- // Don't destroy the parent cache unless the internal data object
- // had been the only thing left in it
- if ( !isEmptyDataObject( cache[ id ] ) ) {
- return;
- }
- }
-
- // Destroy the cache
- if ( isNode ) {
- jQuery.cleanData( [ elem ], true );
-
- // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
- /* jshint eqeqeq: false */
- } else if ( support.deleteExpando || cache != cache.window ) {
- /* jshint eqeqeq: true */
- delete cache[ id ];
-
- // When all else fails, null
- } else {
- cache[ id ] = null;
- }
-}
-
-jQuery.extend({
- cache: {},
-
- // The following elements (space-suffixed to avoid Object.prototype collisions)
- // throw uncatchable exceptions if you attempt to set expando properties
- noData: {
- "applet ": true,
- "embed ": true,
- // ...but Flash objects (which have this classid) *can* handle expandos
- "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
- },
-
- hasData: function( elem ) {
- elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
- return !!elem && !isEmptyDataObject( elem );
- },
-
- data: function( elem, name, data ) {
- return internalData( elem, name, data );
- },
-
- removeData: function( elem, name ) {
- return internalRemoveData( elem, name );
- },
-
- // For internal use only.
- _data: function( elem, name, data ) {
- return internalData( elem, name, data, true );
- },
-
- _removeData: function( elem, name ) {
- return internalRemoveData( elem, name, true );
- }
-});
-
-jQuery.fn.extend({
- data: function( key, value ) {
- var i, name, data,
- elem = this[0],
- attrs = elem && elem.attributes;
-
- // Special expections of .data basically thwart jQuery.access,
- // so implement the relevant behavior ourselves
-
- // Gets all values
- if ( key === undefined ) {
- if ( this.length ) {
- data = jQuery.data( elem );
-
- if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
- i = attrs.length;
- while ( i-- ) {
-
- // Support: IE11+
- // The attrs elements can be null (#14894)
- if ( attrs[ i ] ) {
- name = attrs[ i ].name;
- if ( name.indexOf( "data-" ) === 0 ) {
- name = jQuery.camelCase( name.slice(5) );
- dataAttr( elem, name, data[ name ] );
- }
- }
- }
- jQuery._data( elem, "parsedAttrs", true );
- }
- }
-
- return data;
- }
-
- // Sets multiple values
- if ( typeof key === "object" ) {
- return this.each(function() {
- jQuery.data( this, key );
- });
- }
-
- return arguments.length > 1 ?
-
- // Sets one value
- this.each(function() {
- jQuery.data( this, key, value );
- }) :
-
- // Gets one value
- // Try to fetch any internally stored data first
- elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined;
- },
-
- removeData: function( key ) {
- return this.each(function() {
- jQuery.removeData( this, key );
- });
- }
-});
-
-
-jQuery.extend({
- queue: function( elem, type, data ) {
- var queue;
-
- if ( elem ) {
- type = ( type || "fx" ) + "queue";
- queue = jQuery._data( elem, type );
-
- // Speed up dequeue by getting out quickly if this is just a lookup
- if ( data ) {
- if ( !queue || jQuery.isArray(data) ) {
- queue = jQuery._data( elem, type, jQuery.makeArray(data) );
- } else {
- queue.push( data );
- }
- }
- return queue || [];
- }
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ),
- startLength = queue.length,
- fn = queue.shift(),
- hooks = jQuery._queueHooks( elem, type ),
- next = function() {
- jQuery.dequeue( elem, type );
- };
-
- // If the fx queue is dequeued, always remove the progress sentinel
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- startLength--;
- }
-
- if ( fn ) {
-
- // Add a progress sentinel to prevent the fx queue from being
- // automatically dequeued
- if ( type === "fx" ) {
- queue.unshift( "inprogress" );
- }
-
- // clear up the last queue stop function
- delete hooks.stop;
- fn.call( elem, next, hooks );
- }
-
- if ( !startLength && hooks ) {
- hooks.empty.fire();
- }
- },
-
- // not intended for public consumption - generates a queueHooks object, or returns the current one
- _queueHooks: function( elem, type ) {
- var key = type + "queueHooks";
- return jQuery._data( elem, key ) || jQuery._data( elem, key, {
- empty: jQuery.Callbacks("once memory").add(function() {
- jQuery._removeData( elem, type + "queue" );
- jQuery._removeData( elem, key );
- })
- });
- }
-});
-
-jQuery.fn.extend({
- queue: function( type, data ) {
- var setter = 2;
-
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- setter--;
- }
-
- if ( arguments.length < setter ) {
- return jQuery.queue( this[0], type );
- }
-
- return data === undefined ?
- this :
- this.each(function() {
- var queue = jQuery.queue( this, type, data );
-
- // ensure a hooks for this queue
- jQuery._queueHooks( this, type );
-
- if ( type === "fx" && queue[0] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- });
- },
- dequeue: function( type ) {
- return this.each(function() {
- jQuery.dequeue( this, type );
- });
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
- // Get a promise resolved when queues of a certain type
- // are emptied (fx is the type by default)
- promise: function( type, obj ) {
- var tmp,
- count = 1,
- defer = jQuery.Deferred(),
- elements = this,
- i = this.length,
- resolve = function() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- };
-
- if ( typeof type !== "string" ) {
- obj = type;
- type = undefined;
- }
- type = type || "fx";
-
- while ( i-- ) {
- tmp = jQuery._data( elements[ i ], type + "queueHooks" );
- if ( tmp && tmp.empty ) {
- count++;
- tmp.empty.add( resolve );
- }
- }
- resolve();
- return defer.promise( obj );
- }
-});
-var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
-
-var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
-
-var isHidden = function( elem, el ) {
- // isHidden might be called from jQuery#filter function;
- // in that case, element will be second argument
- elem = el || elem;
- return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
- };
-
-
-
-// Multifunctional method to get and set values of a collection
-// The value/s can optionally be executed if it's a function
-var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
- var i = 0,
- length = elems.length,
- bulk = key == null;
-
- // Sets many values
- if ( jQuery.type( key ) === "object" ) {
- chainable = true;
- for ( i in key ) {
- jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
- }
-
- // Sets one value
- } else if ( value !== undefined ) {
- chainable = true;
-
- if ( !jQuery.isFunction( value ) ) {
- raw = true;
- }
-
- if ( bulk ) {
- // Bulk operations run against the entire set
- if ( raw ) {
- fn.call( elems, value );
- fn = null;
-
- // ...except when executing function values
- } else {
- bulk = fn;
- fn = function( elem, key, value ) {
- return bulk.call( jQuery( elem ), value );
- };
- }
- }
-
- if ( fn ) {
- for ( ; i < length; i++ ) {
- fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
- }
- }
- }
-
- return chainable ?
- elems :
-
- // Gets
- bulk ?
- fn.call( elems ) :
- length ? fn( elems[0], key ) : emptyGet;
-};
-var rcheckableType = (/^(?:checkbox|radio)$/i);
-
-
-
-(function() {
- // Minified: var a,b,c
- var input = document.createElement( "input" ),
- div = document.createElement( "div" ),
- fragment = document.createDocumentFragment();
-
- // Setup
- div.innerHTML = "
a";
-
- // IE strips leading whitespace when .innerHTML is used
- support.leadingWhitespace = div.firstChild.nodeType === 3;
-
- // Make sure that tbody elements aren't automatically inserted
- // IE will insert them into empty tables
- support.tbody = !div.getElementsByTagName( "tbody" ).length;
-
- // Make sure that link elements get serialized correctly by innerHTML
- // This requires a wrapper element in IE
- support.htmlSerialize = !!div.getElementsByTagName( "link" ).length;
-
- // Makes sure cloning an html5 element does not cause problems
- // Where outerHTML is undefined, this still works
- support.html5Clone =
- document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>";
-
- // Check if a disconnected checkbox will retain its checked
- // value of true after appended to the DOM (IE6/7)
- input.type = "checkbox";
- input.checked = true;
- fragment.appendChild( input );
- support.appendChecked = input.checked;
-
- // Make sure textarea (and checkbox) defaultValue is properly cloned
- // Support: IE6-IE11+
- div.innerHTML = "";
- support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
-
- // #11217 - WebKit loses check when the name is after the checked attribute
- fragment.appendChild( div );
- div.innerHTML = "";
-
- // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
- // old WebKit doesn't clone checked state correctly in fragments
- support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- // Support: IE<9
- // Opera does not clone events (and typeof div.attachEvent === undefined).
- // IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
- support.noCloneEvent = true;
- if ( div.attachEvent ) {
- div.attachEvent( "onclick", function() {
- support.noCloneEvent = false;
- });
-
- div.cloneNode( true ).click();
- }
-
- // Execute the test only if not already executed in another module.
- if (support.deleteExpando == null) {
- // Support: IE<9
- support.deleteExpando = true;
- try {
- delete div.test;
- } catch( e ) {
- support.deleteExpando = false;
- }
- }
-})();
-
-
-(function() {
- var i, eventName,
- div = document.createElement( "div" );
-
- // Support: IE<9 (lack submit/change bubble), Firefox 23+ (lack focusin event)
- for ( i in { submit: true, change: true, focusin: true }) {
- eventName = "on" + i;
-
- if ( !(support[ i + "Bubbles" ] = eventName in window) ) {
- // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP)
- div.setAttribute( eventName, "t" );
- support[ i + "Bubbles" ] = div.attributes[ eventName ].expando === false;
- }
- }
-
- // Null elements to avoid leaks in IE.
- div = null;
-})();
-
-
-var rformElems = /^(?:input|select|textarea)$/i,
- rkeyEvent = /^key/,
- rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
- rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
- rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
-
-function returnTrue() {
- return true;
-}
-
-function returnFalse() {
- return false;
-}
-
-function safeActiveElement() {
- try {
- return document.activeElement;
- } catch ( err ) { }
-}
-
-/*
- * Helper functions for managing events -- not part of the public interface.
- * Props to Dean Edwards' addEvent library for many of the ideas.
- */
-jQuery.event = {
-
- global: {},
-
- add: function( elem, types, handler, data, selector ) {
- var tmp, events, t, handleObjIn,
- special, eventHandle, handleObj,
- handlers, type, namespaces, origType,
- elemData = jQuery._data( elem );
-
- // Don't attach events to noData or text/comment nodes (but allow plain objects)
- if ( !elemData ) {
- return;
- }
-
- // Caller can pass in an object of custom data in lieu of the handler
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- selector = handleObjIn.selector;
- }
-
- // Make sure that the handler has a unique ID, used to find/remove it later
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- // Init the element's event structure and main handler, if this is the first
- if ( !(events = elemData.events) ) {
- events = elemData.events = {};
- }
- if ( !(eventHandle = elemData.handle) ) {
- eventHandle = elemData.handle = function( e ) {
- // Discard the second event of a jQuery.event.trigger() and
- // when an event is called after a page has unloaded
- return typeof jQuery !== strundefined && (!e || jQuery.event.triggered !== e.type) ?
- jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
- undefined;
- };
- // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
- eventHandle.elem = elem;
- }
-
- // Handle multiple events separated by a space
- types = ( types || "" ).match( rnotwhite ) || [ "" ];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // There *must* be a type, no attaching namespace-only handlers
- if ( !type ) {
- continue;
- }
-
- // If event changes its type, use the special event handlers for the changed type
- special = jQuery.event.special[ type ] || {};
-
- // If selector defined, determine special event api type, otherwise given type
- type = ( selector ? special.delegateType : special.bindType ) || type;
-
- // Update special based on newly reset type
- special = jQuery.event.special[ type ] || {};
-
- // handleObj is passed to all event handlers
- handleObj = jQuery.extend({
- type: type,
- origType: origType,
- data: data,
- handler: handler,
- guid: handler.guid,
- selector: selector,
- needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
- namespace: namespaces.join(".")
- }, handleObjIn );
-
- // Init the event handler queue if we're the first
- if ( !(handlers = events[ type ]) ) {
- handlers = events[ type ] = [];
- handlers.delegateCount = 0;
-
- // Only use addEventListener/attachEvent if the special events handler returns false
- if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
- // Bind the global event handler to the element
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle, false );
-
- } else if ( elem.attachEvent ) {
- elem.attachEvent( "on" + type, eventHandle );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- // Add to the element's handler list, delegates in front
- if ( selector ) {
- handlers.splice( handlers.delegateCount++, 0, handleObj );
- } else {
- handlers.push( handleObj );
- }
-
- // Keep track of which events have ever been used, for event optimization
- jQuery.event.global[ type ] = true;
- }
-
- // Nullify elem to prevent memory leaks in IE
- elem = null;
- },
-
- // Detach an event or set of events from an element
- remove: function( elem, types, handler, selector, mappedTypes ) {
- var j, handleObj, tmp,
- origCount, t, events,
- special, handlers, type,
- namespaces, origType,
- elemData = jQuery.hasData( elem ) && jQuery._data( elem );
-
- if ( !elemData || !(events = elemData.events) ) {
- return;
- }
-
- // Once for each type.namespace in types; type may be omitted
- types = ( types || "" ).match( rnotwhite ) || [ "" ];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[t] ) || [];
- type = origType = tmp[1];
- namespaces = ( tmp[2] || "" ).split( "." ).sort();
-
- // Unbind all events (on this namespace, if provided) for the element
- if ( !type ) {
- for ( type in events ) {
- jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
- }
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
- type = ( selector ? special.delegateType : special.bindType ) || type;
- handlers = events[ type ] || [];
- tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
-
- // Remove matching events
- origCount = j = handlers.length;
- while ( j-- ) {
- handleObj = handlers[ j ];
-
- if ( ( mappedTypes || origType === handleObj.origType ) &&
- ( !handler || handler.guid === handleObj.guid ) &&
- ( !tmp || tmp.test( handleObj.namespace ) ) &&
- ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
- handlers.splice( j, 1 );
-
- if ( handleObj.selector ) {
- handlers.delegateCount--;
- }
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
- }
-
- // Remove generic event handler if we removed something and no more handlers exist
- // (avoids potential for endless recursion during removal of special event handlers)
- if ( origCount && !handlers.length ) {
- if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- delete events[ type ];
- }
- }
-
- // Remove the expando if it's no longer used
- if ( jQuery.isEmptyObject( events ) ) {
- delete elemData.handle;
-
- // removeData also checks for emptiness and clears the expando if empty
- // so use it instead of delete
- jQuery._removeData( elem, "events" );
- }
- },
-
- trigger: function( event, data, elem, onlyHandlers ) {
- var handle, ontype, cur,
- bubbleType, special, tmp, i,
- eventPath = [ elem || document ],
- type = hasOwn.call( event, "type" ) ? event.type : event,
- namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
-
- cur = tmp = elem = elem || document;
-
- // Don't do events on text and comment nodes
- if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
- return;
- }
-
- // focus/blur morphs to focusin/out; ensure we're not firing them right now
- if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
- return;
- }
-
- if ( type.indexOf(".") >= 0 ) {
- // Namespaced trigger; create a regexp to match event type in handle()
- namespaces = type.split(".");
- type = namespaces.shift();
- namespaces.sort();
- }
- ontype = type.indexOf(":") < 0 && "on" + type;
-
- // Caller can pass in a jQuery.Event object, Object, or just an event type string
- event = event[ jQuery.expando ] ?
- event :
- new jQuery.Event( type, typeof event === "object" && event );
-
- // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
- event.isTrigger = onlyHandlers ? 2 : 3;
- event.namespace = namespaces.join(".");
- event.namespace_re = event.namespace ?
- new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
- null;
-
- // Clean up the event in case it is being reused
- event.result = undefined;
- if ( !event.target ) {
- event.target = elem;
- }
-
- // Clone any incoming data and prepend the event, creating the handler arg list
- data = data == null ?
- [ event ] :
- jQuery.makeArray( data, [ event ] );
-
- // Allow special events to draw outside the lines
- special = jQuery.event.special[ type ] || {};
- if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
- return;
- }
-
- // Determine event propagation path in advance, per W3C events spec (#9951)
- // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
- if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
-
- bubbleType = special.delegateType || type;
- if ( !rfocusMorph.test( bubbleType + type ) ) {
- cur = cur.parentNode;
- }
- for ( ; cur; cur = cur.parentNode ) {
- eventPath.push( cur );
- tmp = cur;
- }
-
- // Only add window if we got to document (e.g., not plain obj or detached DOM)
- if ( tmp === (elem.ownerDocument || document) ) {
- eventPath.push( tmp.defaultView || tmp.parentWindow || window );
- }
- }
-
- // Fire handlers on the event path
- i = 0;
- while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
-
- event.type = i > 1 ?
- bubbleType :
- special.bindType || type;
-
- // jQuery handler
- handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
- if ( handle ) {
- handle.apply( cur, data );
- }
-
- // Native handler
- handle = ontype && cur[ ontype ];
- if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
- event.result = handle.apply( cur, data );
- if ( event.result === false ) {
- event.preventDefault();
- }
- }
- }
- event.type = type;
-
- // If nobody prevented the default action, do it now
- if ( !onlyHandlers && !event.isDefaultPrevented() ) {
-
- if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
- jQuery.acceptData( elem ) ) {
-
- // Call a native DOM method on the target with the same name name as the event.
- // Can't use an .isFunction() check here because IE6/7 fails that test.
- // Don't do default actions on window, that's where global variables be (#6170)
- if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) {
-
- // Don't re-trigger an onFOO event when we call its FOO() method
- tmp = elem[ ontype ];
-
- if ( tmp ) {
- elem[ ontype ] = null;
- }
-
- // Prevent re-triggering of the same event, since we already bubbled it above
- jQuery.event.triggered = type;
- try {
- elem[ type ]();
- } catch ( e ) {
- // IE<9 dies on focus/blur to hidden element (#1486,#12518)
- // only reproducible on winXP IE8 native, not IE9 in IE8 mode
- }
- jQuery.event.triggered = undefined;
-
- if ( tmp ) {
- elem[ ontype ] = tmp;
- }
- }
- }
- }
-
- return event.result;
- },
-
- dispatch: function( event ) {
-
- // Make a writable jQuery.Event from the native event object
- event = jQuery.event.fix( event );
-
- var i, ret, handleObj, matched, j,
- handlerQueue = [],
- args = slice.call( arguments ),
- handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [],
- special = jQuery.event.special[ event.type ] || {};
-
- // Use the fix-ed jQuery.Event rather than the (read-only) native event
- args[0] = event;
- event.delegateTarget = this;
-
- // Call the preDispatch hook for the mapped type, and let it bail if desired
- if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
- return;
- }
-
- // Determine handlers
- handlerQueue = jQuery.event.handlers.call( this, event, handlers );
-
- // Run delegates first; they may want to stop propagation beneath us
- i = 0;
- while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
- event.currentTarget = matched.elem;
-
- j = 0;
- while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
-
- // Triggered event must either 1) have no namespace, or
- // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
- if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
-
- event.handleObj = handleObj;
- event.data = handleObj.data;
-
- ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
- .apply( matched.elem, args );
-
- if ( ret !== undefined ) {
- if ( (event.result = ret) === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
- }
-
- // Call the postDispatch hook for the mapped type
- if ( special.postDispatch ) {
- special.postDispatch.call( this, event );
- }
-
- return event.result;
- },
-
- handlers: function( event, handlers ) {
- var sel, handleObj, matches, i,
- handlerQueue = [],
- delegateCount = handlers.delegateCount,
- cur = event.target;
-
- // Find delegate handlers
- // Black-hole SVG