Directory list view with clipboard. New uploads are added to the clipboard
and can then be filed into folders.
Custom model fields are provided for use in 3rd party apps as a replacement for
the default FileField from django. Behind the scenes a ForeignKey to the
File model is used.
Please make sure you install Pillow with JPEG and ZLIB support installed;
for further information on Pillow installation and its binary dependencies,
check Pillow doc.
Note that easy_thumbnails also has database tables and needs a syncdb or
migrate.
For easy_thumbnails to support retina displays (recent MacBooks, iOS) add to settings.py:
THUMBNAIL_HIGH_RESOLUTION=True
If you forget this, you may not see thumbnails for your uploaded files. Adding this line and
refreshing the admin page will create the missing thumbnails.
django-filer javascript and css files are managed by django.contrib.staticfiles;
please see staticfiles documentation to know how to deploy filer static files
in your environment.
It is possible to define the important part of an image (the
subject location) in the admin interface for django-filer images. This is
very useful when later resizing and cropping images with easy_thumbnails. The
image can then be cropped automatically in a way, that the important part of
the image is always visible.
To enable automatic subject location aware cropping of images replace
easy_thumbnails.processors.scale_and_crop with
filer.thumbnail_processors.scale_and_crop_with_subject_location in the
THUMBNAIL_PROCESSORS setting:
Contrary to the file’s actual URL, the canonical URL does not change if you upload a new version of the file.
Thus, you can safely share the canonical URL. As long as the file exists, people will be redirected to its
latest version.
The canonical URL is displayed in the “advanced” panel on the file’s admin page. It has the form:
/filer/canonical/1442488644/12/
The “filer” part of the URL is configured in the project’s URLconf as described above. The “canonical” part can be
changed with the setting FILER_CANONICAL_URL, which defaults to 'canonical/'. Example:
While by default django-filer usually silently skips icon/thumbnail
generation errors, two options are provided to help when working with django-filer:
FILER_DEBUG: Boolean, controls whether bubbling up any easy-thumbnails
exception (typically if an image file doesn’t exists); is False by default;
FILER_ENABLE_LOGGING: Boolean, controls whether logging the above exceptions.
It requires proper django logging configuration for default logger or
filer logger. Please see https://docs.djangoproject.com/en/dev/topics/logging/
for further information about Django’s logging configuration.
0.9 introduces real separation of private and public files through multiple storage backends. Public files are placed
inside MEDIA_ROOT, using Djangos default file storage. Private files are now placed in their own location.
Unfortunatly the default settings in django-filer 0.8.x made all new uploads “private”, but still placed them inside
MEDIA_ROOT in a subfolder called filer_private. In most cases these files are actually meant to be public,
so they should be moved.
Note
Quick and Dirty: set FILER_0_8_COMPATIBILITY_MODE=True. It will pick up the old style settings and
configure storage backends the way they were in 0.8. This setting is only meant to easy migration and is
not intended to be used long-term.
Fire up the sql-console and change is_public to True on all files in the
filer_file table (UPDATEfiler_fileSETis_public=1WHEREis_public=0;). The files will still be in
MEDIA_ROOT/filer_private/, but serving them should already work. Then you can move the files
into filer_private in the filesystem and update the corresponding paths in the database.
Have filer move all files between storages. This might take a while, since django will read
each file into memory and write it to the new location. Especially if you are using an external storage backend
such as Amazon S3, this might not be an option.
Set FILER_0_8_COMPATIBILITY_MODE=True and make sure you can access public and private files. Then run this
snippet in the django shell:
fromfiler.modelsimportFileimportsysforfinFile.objects.filter(is_public=False):sys.stdout.write(u'moving %s to public storage... '%f.id)f.is_public=Truef.save()sys.stdout.write(u'done\n')
After running the script you can delete the FILER_0_8_COMPATIBILITY_MODE setting. If you want to use secure
downloads see Secure Downloads.
In develop pre-0.9a3 file path was written in the database as relative path inside filer directory; since 0.9a3
this is no longer the case so file must be migrate to the new paths.
Same disclaimer as 0.8x migration applies: SQL migration is much faster for large datasets.
Use whatever tool to access you database console and insert the correct directory name at the start of the file field.
Example:
UPDATE filer_file SET file= 'filer_public/' || file WHERE file LIKE '20%' AND is_public=True;
UPDATE filer_file SET file= 'filer_private/' || file WHERE file LIKE '20%' AND is_public=False;
Then you will have to move by hand the files from the MEDIA_ROOT/filer directory to the new public and private storage
directories
Make sure the console user can access/write public and private files.
Please note that the “filer/” string below should be modified if your files are not saved in MEDIA_ROOT/filer
Then run this snippet in the django shell:
fromfiler.modelsimportFileimportsysforfinFile.objects.filter(is_public=True):sys.stdout.write(u'moving %s to public storage... '%f.id)f.is_public=Falsef.file.name="filer/%s"%f.file.namef.save()f.is_public=Truef.save()sys.stdout.write(u'done\n')forfinFile.objects.filter(is_public=False):sys.stdout.write(u'moving %s to private storage... '%f.id)f.is_public=Truef.file.name="filer/%s"%f.file.namef.save()f.is_public=Falsef.save()sys.stdout.write(u'done\n')
Double access modification is needed to enabled automatic file move.
django-filer provides model fields to replace djangos own
django.db.models.FileField and django.db.models.ImageField.
The django-filer versions provide the added benefit of being able to manage
the files independently of where they are actually used in your content. As such
the same file can be used in multiple places without re-uploading it multiple
times and wasting bandwidth, time and storage.
It also comes with additional tools to detect file duplicates based on SHA1
checksums.
Note
behind the scenes this field is actually just a ForeignKey to the File model
in django-filer. So you can easily access the extra metadata like this:
company.disclaimer.sha1company.disclaimer.sizecompany.logo.widthcompany.logo.heightcompany.logo.icons['64']# or {{ company.logo.icons.64 }} in a templatecompany.logo.url# prints path to original image
They are subclasses of django.db.models.ForeignKey, so the same rules apply.
The only difference is, that there is no need to declare what model we are
referencing (it is always filer.models.File for the FilerFileField and
filer.models.Image for the FilerImageField).
The default widget provides a popup file selector that also directly supports
uploading new images.
Clicking on the magnifying glass will display the file selction popup.
The red X will de-select the currently selected file (usefull if the field
can be null).
Warning
Don’t place a FilerFileField as the first field in admin. Django admin
will try to set the focus to the first field in the form. But since the form
field of FilerFileField is hidden that will cause in a javascript error.
File download permissions are an experimental feature. The api may change at any time.
By default files can be uploaded and managed by all staff members based on the
standard django model permissions.
Activating permission checking with the FILER_ENABLE_PERMISSIONS setting enables
fine grained permissions based on individual folders.
Permissions can be set in the “Folder permissions” section in Django admin.
Note
These permissions only concern editing files and folders in Django admin. All the files are
still world downloadable by anyone who guesses the url. For real permission checks on downloads
see the Secure Downloads section.
Secure downloads are experimental and the API may change at any time.
Warning
Server Backends currently only work with files in the local filesystem.
Note
For the impatient:
set FILER_ENABLE_PERMISSIONS to True
include filer.server.urls in the root urls.py without a
prefix
To be able to check permissions on the file downloads, a special view is used.
The files are saved in a separate location outside of MEDIA_ROOT to prevent
accidental serving. By default this is a directory called smedia that is
located in the parent directory of MEDIA_ROOT.
The smedia directory must NOT be served by the webserver directly, because
that would bypass the permission checks.
To hook up the view filer.server.urls needs to be included in the root
urls.py:
Files with restricted permissions need to be placed in a secure storage backend.
Configure a secure storage backend in FILER_STORAGES or use the default.
Warning
The “Permissions disabled” checkbox in the file detail view in Django admin
controls in which storage backend the file is saved. In order for it to be
protected, this field must not be checked.
For images the permissions also extend to all generated thumbnails.
By default files with permissions are served directly by the Django process (using the
filer.server.backends.default.DefaultServer backend). That is
acceptable in a development environment, but is very bad for performance and security in
production.
The private file view will serve the permission-checked media files by
delegating to one of its server backends. The ones bundled with django-filer
live in filer.server.backends and it is easy to create new ones.
The default is filer.server.backends.default.DefaultServer. It is suitable
for development and serves the file directly from django.
More suitiable for production are server backends that delegate the actual file
serving to an upstream webserver.
nginx_location is the location directive where nginx “hides”
permission-checked files from general access. A fitting nginx configuration
might look something like this:
location /nginx_filer_private/ {
internal;
alias /path/to/smedia/filer_private/;
}
location /nginx_filer_private_thumbnails/ {
internal;
alias /path/to/smedia/filer_private_thumbnails/;
}
Note
make sure you follow the example exactly. Missing trailing slashes and
alias vs. root have subtle differences that can make your config
fail.
NginxXAccelRedirectServer will add the a X-Accel-Redirect header to
the response instead of actually loading and delivering the file itself. The
value in the header will be something like
/nginx_filer_private/2011/03/04/myfile.pdf. Nginx picks this up and does
the actual file delivery while the django backend is free to do other stuff
again.
Activate the or not the Permission check on the files and folders before
displaying them in the admin. When set to False it gives all the authorization
to staff members based on standard Django model permissions.
Defaults to FileSystemStorage in <MEDIA_ROOT>/filer_public/ and <MEDIA_ROOT>/filer_public_thumbnails/ for public files and
<MEDIA_ROOT>/../smedia/filer_private/ and <MEDIA_ROOT>/../smedia/filer_private_thumbnails/ for private files.
Public storage uses DEFAULT_FILE_STORAGE as default storage backend.
UPLOAD_TO is the function to generate the path relative to the storage root. The
default generates a random path like 1d/a5/1da50fee-5003-46a1-a191-b547125053a8/filename.jpg. This
will be applied whenever a file is uploaded or moved between public (without permission checks) and
private (with permission checks) storages. Defaults to 'filer.utils.generate_filename.randomized'.
Regular users are not allowed to create new folders at the root level, only
subfolders of already existing folders, unless this setting is set to True.
Django Filer ships with support for image files, and generic files (everything
that’s not an image).
So what if you wanted to add support for a particular kind of file thats’s not already included?
It’s easy to extend it to do this, without needing to touch the Filer’s code at all
(and no-one wants to have to maintain fork if they can avoid it).
So for example, you might want to be able to manage video files. You could of
course simply store and file them as generic file types, but that might not be
enough - perhaps your own application needs to know that certain files are in
fact video files, so that it can treat them appropriately (process them, allow
only them to be selected in certain widgets, and so on).
In this example we will create support for video files.
In your own application, you need to create a Video model. This model has to inherit from filer.models.filemodels.File.
# import the File class to inherit fromfromfiler.models.filemodelsimportFile# we'll need to refer to filer settingsfromfilerimportsettingsasfiler_settingsclassVideo(File):pass# for now...
When a file is uploaded, filer.admin.clipboardadmin.ClipboardAdmin.ajax_upload() loops over the different classes in filer.settings.FILER_FILE_MODELS and calls its matches_file_type() to see if the file matches a known filename extension.
When a match is found, the filer will create an instance of that class for the file.
So let’s add a matches_file_type() method to the Video model:
@classmethoddefmatches_file_type(cls,iname,ifile,request):# the extensions we'll recognise for this file typefilename_extensions=['.dv','.mov','.mp4','.avi','.wmv',]ext=os.path.splitext(iname)[1].lower()returnextinfilename_extensions
Now you can upload files of those types into the Filer.
For each one you upload an instance of your Video class will be created.
At the moment, the files you upload will have the Filer’s generic file icon - not very appropriate or helpful for video. What you need to do is add a suitable _icon attribute to the class.
The filer.models.filemodels.File class we’ve inherited from has an icons() property, from filer.models.mixins.IconsMixin.
This checks for the _icon attribute; if it finds one, it uses it to build URLs for the icons in various different sizes. If _icons is video, a typical result might be /static/filer/icons/video_48x48.png.
Of course, you can also create an icons() property specific to your new model. For example, filer.models.imagemodels.Image does that, so that it can create thumbnail icons for each file rather than a single icon for all of that type.
In our Video model the simple case will do:
# the icon it will use_icon="video"
And in fact, the Filer already has an icon that matches this - if there were not already a set of video icons in the Filer’s static assets, we’d have to provide them - see filer/static/icons for examples.
Now we need to register our new model with the admin. Again, the very simplest case:
fromdjango.contribimportadminfromfiler.admin.fileadminimportFileAdminfrommodelsimportVideoadmin.site.register(Video,FileAdmin)# use the standard FileAdmin
... but of course if your model had particular fields of its own (as for example the Image model has a subject_location field) you would create your own ModelAdmin class for it, along with a form, special widgets and whatever else you needed.
You’ve now done enough to be able to get hold of files of your new kind in the admin (wherever the admin uses a FilerFileField) but to make it really useful we need to to a little more.
For example, it might be useful to have:
its own field type to get hold of it in some other model
a special form for the field
a widget for selecting it in the admin
... and so on
How you use it will be up to you, but a fairly typical use case would be in a django CMS plugin, and that is the example that will be followed here.
filer/templates/templates/admin/filer/folder lists the individual items in each folder.
It checks item.file_type to determine how to display those items and what to display for them.
You might want to extend this, so that the list includes the appropriate information for your new file type.
In that case you will need to override the template, and in the Video model:
# declare the file_type for the list templatefile_type='Video'
Note that if you do this, you will need to override the template - otherwise your items will fail to display in the folder lists.
By default, filer will search against name for Folders and name, description, and
original_filename for Files, in
addition to searching against the owner. If you are using auth.User as
your User model, filer will search against the username, first_name,
last_name, email fields. If you are using a custom User model, filer
will search against all fields that are CharFields except for the password
field. You can override this behavior by subclassing the
filer.admin.folderadmin.FolderAdmin class and overriding the
owner_search_fields property.
# in an admin.py filefromdjango.contribimportadminfromfiler.adminimportFolderAdminfromfiler.modelsimportFolderclassMyFolderAdmin(FolderAdmin):owner_search_fields=['field1','field2']admin.site.unregister(Folder)admin.site.register(Folder,FolderAdmin)
You can also override the search behavior for Folders.
Just override search_fields by subclassing
the filer.admin.folderadmin.FolderAdmin. It works as described in
Django’s docs. E.g.:
# in an admin.py fileclassMyFolderAdmin(FolderAdmin):search_fields=['=field1','^field2']admin.site.unregister(Folder)admin.site.register(Folder,MyFolderAdmin)
First a custom model must be defined; it should inherit from BaseImage, the basic abstract class:
fromfiler.models.abstract.BaseImageclassCustomImage(BaseImage):my_field=models.CharField(max_length=10)classMeta:# You must define a meta with en explicit app_labelapp_label='myapp'
The model can be defined in any installed application declared afterdjango-filer.
BaseImage defines the following fields (plus the basic fields defined in File):
default_alt_text
default_caption
subject_location
you may add whatever fields you need, just like any other model.
..warning: app_label in Meta must be explicitly defined.
If you added fields in your custom Image model, you have to customize the admin too:
fromdjango.contribimportadminfromfiler.admin.imageadminimportImageAdminfromfiler.models.imagemodelsimportImageclassCustomImageAdmin(ImageAdmin):# your custom codepass# Using build_fieldsets allows to easily integrate common field in the admin# Don't define fieldsets in the ModelAdmin above and add the custom fields# to the ``extra_main_fields`` or ``extra_fieldsets`` as shown belowCustomImageAdmin.fieldsets=CustomImageAdmin.build_fieldsets(extra_main_fields=('default_alt_text','default_caption','my_field'...),extra_fieldsets=(('Subject Location',{'fields':('subject_location',),'classes':('collapse',),}),))# Unregister the default adminadmin.site.unregister(ImageAdmin)# Register your ownadmin.site.register(Image,CustomImageAdmin)
django-filer is continuously being tested on travis-ci.
The simplest way to run the testsuite locally is to checkout the sourcecode, make sure you have Pillow installed and
run:
python setup.py test
It is also possible to invoke the test script directly. Just make sure the test dependencies have been installed:
runtests.py
The recommended way to test locally is with tox. Once tox is installed,
simply running the tox command inside the package root. You don’t need to bother with any virtualenvs, it will be
done for you. Tox will setup multiple virtual environments with different python and django versions to test against:
# run all tests in all default environments
tox
# run testsuite with django-dev/python 2.7 and django-1.4/python 2.6
tox -e py27-django-dev,py26-django14
# run a specific testcase in all environemnts
tox -- filer.FilerApiTests.test_create_folder_structure
# run a test class in specific environments
tox -e py27-django-dev,py26-django14 -- filer.FilerApiTests
--verbosity=3 and --failfast are also supported.
To speed things up a bit use detox. detox runs each testsuite in a
separate process in parallel.
django-filer stores the meta-data of each file in the database, while the files payload is
stored on disk. This is fine, since large binary data shall only exceptionally be stored in a
relational database. The consequence however is, that when invoking managedumpdata only the
meta-data is dumped, while the payload remains on disk. During backups this can be a problem, since
the payload must be handled though other means, for example tar or zip.
django-filer has a feature, which allows to dump the files payload together with their
meta-data. This is achieved by converting the payload into a BASE64 string which in consequence is
added to the dumped data. The advantage is, that the dumped file can be imported without having to
fiddle with zip, tar and file pathes.
In order to activate this feature, add:
FILER_DUMP_PAYLOAD=True
to the projects settings.py file.
If the content has been dumped together with to payload, the files are restored when using
manage.pyloaddata. If the payload is missing, only the meta-data is restored. This is the
default behavior.
It simplifies backups and migrations, since the data entered into the content management system
is dumped into one single file.
If the directory filer_public is missing, django-filer rebuilds the file tree from
scratch. This can be used to get rid of zombie files, such as generated thumbnails which are not
used any more.
When dumping the filers content, you get warned about missing files.
When dumping the filers content, the checksum of the dumped file is compared to that generated
during the primary file upload. In case the checksum diverges, you will be warned.
Only the uploaded file is dumped. Thumbnails derived from the uploaded files will be regenerated
by django-filer when required. This saves some space during backups.
If you dumped a whole database, and not only a partial application, then you may encounter problems
with primary key conflicts during an import:
Could not load contenttypes.ContentType(...)
To circumvent this, first you must flush the whole database’s content. Using the management command
./manage.pyflush, does not truncate all tables: This is because the content in the table
django_content_type is reset to the state after initializing the database using
./manage.pysyncdb and thus is not empty.
").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
PK jG` ` . django-filer-1.0.2/_static/underscore-1.3.1.js// Underscore.js 1.3.1
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice,
unshift = ArrayProto.unshift,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); };
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root['_'] = _;
}
// Current version.
_.VERSION = '1.3.1';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
for (var key in obj) {
if (_.has(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
}
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
if (obj.length === +obj.length) results.length = obj.length;
return results;
};
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError('Reduce of empty array with no initial value');
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var reversed = _.toArray(obj).reverse();
if (context && !initial) iterator = _.bind(iterator, context);
return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
each(obj, function(value, index, list) {
if (!iterator.call(context, value, index, list)) results[results.length] = value;
});
return results;
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if a given value is included in the array or object using `===`.
// Aliased as `contains`.
_.include = _.contains = function(obj, target) {
var found = false;
if (obj == null) return found;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
found = any(obj, function(value) {
return value === target;
});
return found;
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
return _.map(obj, function(value) {
return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Return the maximum element or (element-based computation).
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array.
_.shuffle = function(obj) {
var shuffled = [], rand;
each(obj, function(value, index, list) {
if (index == 0) {
shuffled[0] = value;
} else {
rand = Math.floor(Math.random() * (index + 1));
shuffled[index] = shuffled[rand];
shuffled[rand] = value;
}
});
return shuffled;
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = function(obj, val) {
var result = {};
var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
each(obj, function(value, index) {
var key = iterator(value, index);
(result[key] || (result[key] = [])).push(value);
});
return result;
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
iterator || (iterator = _.identity);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
// Safely convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return slice.call(iterable);
if (_.isArguments(iterable)) return slice.call(iterable);
return _.values(iterable);
};
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head`. The **guard** check allows it to work
// with `_.map`.
_.first = _.head = function(array, n, guard) {
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the last entry of the array. Especcialy useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if ((n != null) && !guard) {
return slice.call(array, Math.max(array.length - n, 0));
} else {
return array[array.length - 1];
}
};
// Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = function(array, index, guard) {
return slice.call(array, (index == null) || guard ? 1 : index);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, function(value){ return !!value; });
};
// Return a completely flattened version of an array.
_.flatten = function(array, shallow) {
return _.reduce(array, function(memo, value) {
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
memo[memo.length] = value;
return memo;
}, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator) {
var initial = iterator ? _.map(array, iterator) : array;
var result = [];
_.reduce(initial, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
memo[memo.length] = el;
result[result.length] = array[i];
}
return memo;
}, []);
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays. (Aliased as "intersect" for back-compat.)
_.intersection = _.intersect = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = _.flatten(slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.include(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i, l;
if (isSorted) {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) {
if (array == null) return -1;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (i in array && array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var len = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(len);
while(idx < len) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as `curry`.
// Delegates to **ECMAScript 5**'s native `Function.bind` if available.
// We check for `func.bind` first, to fail fast when `func` is undefined.
_.bind = function bind(func, context) {
var bound, args;
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
var context, args, timeout, throttling, more;
var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
return function() {
context = this; args = arguments;
var later = function() {
timeout = null;
if (more) func.apply(context, args);
whenDone();
};
if (!timeout) timeout = setTimeout(later, wait);
if (throttling) {
more = true;
} else {
func.apply(context, args);
}
whenDone();
throttling = true;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function(func, wait) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
func.apply(context, args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
return memo = func.apply(this, arguments);
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func].concat(slice.call(arguments, 0));
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
if (times <= 0) return func();
return function() {
if (--times < 1) { return func.apply(this, arguments); }
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
return _.map(obj, _.identity);
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
obj[prop] = source[prop];
}
});
return obj;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
for (var prop in source) {
if (obj[prop] == null) obj[prop] = source[prop];
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function.
function eq(a, b, stack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a._chain) a = a._wrapped;
if (b._chain) b = b._wrapped;
// Invoke a custom `isEqual` method if one is provided.
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = stack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (stack[length] == a) return true;
}
// Add the first object to the stack of traversed objects.
stack.push(a);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
// Ensure commutative equality for sparse arrays.
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
}
}
} else {
// Objects with different constructors are not equivalent.
if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
stack.pop();
return result;
}
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Is a given variable an arguments object?
_.isArguments = function(obj) {
return toString.call(obj) == '[object Arguments]';
};
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Is a given value a function?
_.isFunction = function(obj) {
return toString.call(obj) == '[object Function]';
};
// Is a given value a string?
_.isString = function(obj) {
return toString.call(obj) == '[object String]';
};
// Is a given value a number?
_.isNumber = function(obj) {
return toString.call(obj) == '[object Number]';
};
// Is the given value `NaN`?
_.isNaN = function(obj) {
// `NaN` is the only value for which `===` is not reflexive.
return obj !== obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value a date?
_.isDate = function(obj) {
return toString.call(obj) == '[object Date]';
};
// Is the given value a regular expression?
_.isRegExp = function(obj) {
return toString.call(obj) == '[object RegExp]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Has own property?
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function (n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i);
};
// Escape a string for HTML interpolation.
_.escape = function(string) {
return (''+string).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
};
// Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) {
each(_.functions(obj), function(name){
addToWrapper(name, _[name] = obj[name]);
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /.^/;
// Within an interpolation, evaluation, or escaping, remove HTML escaping
// that had been previously added.
var unescape = function(code) {
return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(str, data) {
var c = _.templateSettings;
var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' +
str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.escape || noMatch, function(match, code) {
return "',_.escape(" + unescape(code) + "),'";
})
.replace(c.interpolate || noMatch, function(match, code) {
return "'," + unescape(code) + ",'";
})
.replace(c.evaluate || noMatch, function(match, code) {
return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n')
.replace(/\t/g, '\\t')
+ "');}return __p.join('');";
var func = new Function('obj', '_', tmpl);
if (data) return func(data, _);
return function(data) {
return func.call(this, data, _);
};
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// The OOP Wrapper
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; };
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
};
// A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() {
var args = slice.call(arguments);
unshift.call(args, this._wrapped);
return result(func.apply(_, args), this._chain);
};
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
var wrapped = this._wrapped;
method.apply(wrapped, arguments);
var length = wrapped.length;
if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
return result(wrapped, this._chain);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
};
});
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
}).call(this);
PK jG'
5w
, django-filer-1.0.2/_static/comment-close.pngPNG
IHDR a
OiCCPPhotoshop ICC profile xڝSgTS=BKKoR RB&*! J!QEEȠQ,
!{kּ>H3Q5B.@
$p d!s# ~<<+" x M0B\t8K @zB @F&S