From ccdc04b8af41afb933437c39e6d953a4b4558b8a Mon Sep 17 00:00:00 2001 From: Jordan OMara Date: Tue, 9 Sep 2014 09:26:20 -0400 Subject: [PATCH] First commit of fileupload packaging --- MANIFEST.in | 8 + README.txt | 13 + setup.py | 27 ++ xstatic/__init__.py | 1 + xstatic/pkg/__init__.py | 1 + xstatic/pkg/angular_fileupload/__init__.py | 49 ++++ .../data/angular-file-upload.js | 250 ++++++++++++++++++ 7 files changed, 349 insertions(+) create mode 100644 MANIFEST.in create mode 100644 README.txt create mode 100644 setup.py create mode 100644 xstatic/__init__.py create mode 100644 xstatic/pkg/__init__.py create mode 100644 xstatic/pkg/angular_fileupload/__init__.py create mode 100644 xstatic/pkg/angular_fileupload/data/angular-file-upload.js diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..88c8d70 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,8 @@ +include README.txt +recursive-include xstatic/pkg/angular_fileupload * + +global-exclude *.pyc +global-exclude *.pyo +global-exclude *.orig +global-exclude *.rej + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..1f327c5 --- /dev/null +++ b/README.txt @@ -0,0 +1,13 @@ +XStatic-Angular-Filepload +------------------------- + +Angular-FileUpload JavaScript library packaged for setuptools (easy_install) / pip. + +This package is intended to be used by **any** project that needs these files. + +It intentionally does **not** provide any extra code except some metadata +**nor** has any extra requirements. You MAY use some minimal support code from +the XStatic base package, if you like. + +You can find more info about the xstatic packaging way in the package `XStatic`. + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7ed6188 --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +from xstatic.pkg import angular_fileupload as xs + +# The README.txt file should be written in reST so that PyPI can use +# it to generate your project's PyPI page. +long_description = open('README.txt').read() + +from setuptools import setup, find_packages + +setup( + name=xs.PACKAGE_NAME, + version=xs.PACKAGE_VERSION, + description=xs.DESCRIPTION, + long_description=long_description, + classifiers=xs.CLASSIFIERS, + keywords=xs.KEYWORDS, + maintainer=xs.MAINTAINER, + maintainer_email=xs.MAINTAINER_EMAIL, + license=xs.LICENSE, + url=xs.HOMEPAGE, + platforms=xs.PLATFORMS, + packages=find_packages(), + namespace_packages=['xstatic', 'xstatic.pkg', ], + include_package_data=True, + zip_safe=False, + install_requires=[], # nothing! :) + # if you like, you MAY use the 'XStatic' package. +) diff --git a/xstatic/__init__.py b/xstatic/__init__.py new file mode 100644 index 0000000..de40ea7 --- /dev/null +++ b/xstatic/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/xstatic/pkg/__init__.py b/xstatic/pkg/__init__.py new file mode 100644 index 0000000..de40ea7 --- /dev/null +++ b/xstatic/pkg/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/xstatic/pkg/angular_fileupload/__init__.py b/xstatic/pkg/angular_fileupload/__init__.py new file mode 100644 index 0000000..00a2ebc --- /dev/null +++ b/xstatic/pkg/angular_fileupload/__init__.py @@ -0,0 +1,49 @@ +""" +XStatic resource package + +See package 'XStatic' for documentation and basic tools. +""" + +DISPLAY_NAME = 'Angular-Bootstrap' # official name, upper/lowercase allowed, no spaces +PACKAGE_NAME = 'XStatic-%s' % DISPLAY_NAME # name used for PyPi + +NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar') + # please use a all-lowercase valid python + # package name + +VERSION = '0.11.0' # version of the packaged files, please use the upstream + # version number +BUILD = '2' # our package build number, so we can release new builds + # with fixes for xstatic stuff. +PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi + +DESCRIPTION = "%s %s (XStatic packaging standard)" % (DISPLAY_NAME, VERSION) + +PLATFORMS = 'any' +CLASSIFIERS = [] +KEYWORDS = '%s xstatic' % NAME + +# XStatic-* package maintainer: +MAINTAINER = 'Jordan OMara' +MAINTAINER_EMAIL = 'jsomara@gmail.com' + +# this refers to the project homepage of the stuff we packaged: +HOMEPAGE = 'https://github.com/danialfarid/angular-file-upload' + +# this refers to all files: +LICENSE = '(same as %s)' % DISPLAY_NAME + +from os.path import join, dirname +BASE_DIR = join(dirname(__file__), 'data') +# linux package maintainers just can point to their file locations like this: +#BASE_DIR = '/usr/share/javascript/angular_cookies' + +LOCATIONS = { + # CDN locations (if no public CDN exists, use an empty dict) + # if value is a string, it is a base location, just append relative + # path/filename. if value is a dict, do another lookup using the + # relative path/filename you want. + # your relative path/filenames should usually be without version + # information, because either the base dir/url is exactly for this + # version or the mapping will care for accessing this version. +} diff --git a/xstatic/pkg/angular_fileupload/data/angular-file-upload.js b/xstatic/pkg/angular_fileupload/data/angular-file-upload.js new file mode 100644 index 0000000..2aa65fc --- /dev/null +++ b/xstatic/pkg/angular_fileupload/data/angular-file-upload.js @@ -0,0 +1,250 @@ +/**! + * AngularJS file upload/drop directive with http post and progress + * @author Danial + * @version 1.4.0 + */ +(function() { + +var angularFileUpload = angular.module('angularFileUpload', []); + +angularFileUpload.service('$upload', ['$http', '$timeout', function($http, $timeout) { + function sendHttp(config) { + config.method = config.method || 'POST'; + config.headers = config.headers || {}; + config.transformRequest = config.transformRequest || function(data, headersGetter) { + if (window.ArrayBuffer && data instanceof window.ArrayBuffer) { + return data; + } + return $http.defaults.transformRequest[0](data, headersGetter); + }; + + if (window.XMLHttpRequest.__isShim) { + config.headers['__setXHR_'] = function() { + return function(xhr) { + if (!xhr) return; + config.__XHR = xhr; + config.xhrFn && config.xhrFn(xhr); + xhr.upload.addEventListener('progress', function(e) { + if (config.progress) { + $timeout(function() { + if(config.progress) config.progress(e); + }); + } + }, false); + //fix for firefox not firing upload progress end, also IE8-9 + xhr.upload.addEventListener('load', function(e) { + if (e.lengthComputable) { + if(config.progress) config.progress(e); + } + }, false); + }; + }; + } + + var promise = $http(config); + + promise.progress = function(fn) { + config.progress = fn; + return promise; + }; + promise.abort = function() { + if (config.__XHR) { + $timeout(function() { + config.__XHR.abort(); + }); + } + return promise; + }; + promise.xhr = function(fn) { + config.xhrFn = fn; + return promise; + }; + promise.then = (function(promise, origThen) { + return function(s, e, p) { + config.progress = p || config.progress; + var result = origThen.apply(promise, [s, e, p]); + result.abort = promise.abort; + result.progress = promise.progress; + result.xhr = promise.xhr; + result.then = promise.then; + return result; + }; + })(promise, promise.then); + + return promise; + } + + this.upload = function(config) { + config.headers = config.headers || {}; + config.headers['Content-Type'] = undefined; + config.transformRequest = config.transformRequest || $http.defaults.transformRequest; + var formData = new FormData(); + var origTransformRequest = config.transformRequest; + var origData = config.data; + config.transformRequest = function(formData, headerGetter) { + if (origData) { + if (config.formDataAppender) { + for (var key in origData) { + var val = origData[key]; + config.formDataAppender(formData, key, val); + } + } else { + for (var key in origData) { + var val = origData[key]; + if (typeof origTransformRequest == 'function') { + val = origTransformRequest(val, headerGetter); + } else { + for (var i = 0; i < origTransformRequest.length; i++) { + var transformFn = origTransformRequest[i]; + if (typeof transformFn == 'function') { + val = transformFn(val, headerGetter); + } + } + } + formData.append(key, val); + } + } + } + + if (config.file != null) { + var fileFormName = config.fileFormDataName || 'file'; + + if (Object.prototype.toString.call(config.file) === '[object Array]') { + var isFileFormNameString = Object.prototype.toString.call(fileFormName) === '[object String]'; + for (var i = 0; i < config.file.length; i++) { + formData.append(isFileFormNameString ? fileFormName + i : fileFormName[i], config.file[i], config.file[i].name); + } + } else { + formData.append(fileFormName, config.file, config.file.name); + } + } + return formData; + }; + + config.data = formData; + + return sendHttp(config); + }; + + this.http = function(config) { + return sendHttp(config); + } +}]); + +angularFileUpload.directive('ngFileSelect', [ '$parse', '$timeout', function($parse, $timeout) { + return function(scope, elem, attr) { + var fn = $parse(attr['ngFileSelect']); + elem.bind('change', function(evt) { + var files = [], fileList, i; + fileList = evt.target.files; + if (fileList != null) { + for (i = 0; i < fileList.length; i++) { + files.push(fileList.item(i)); + } + } + $timeout(function() { + fn(scope, { + $files : files, + $event : evt + }); + }); + }); + // removed this since it was confusing if the user click on browse and then cancel #181 +// elem.bind('click', function(){ +// this.value = null; +// }); + + // touch screens + if (('ontouchstart' in window) || + (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) { + elem.bind('touchend', function(e) { + e.preventDefault(); + e.target.click(); + }); + } + }; +} ]); + +angularFileUpload.directive('ngFileDropAvailable', [ '$parse', '$timeout', function($parse, $timeout) { + return function(scope, elem, attr) { + if ('draggable' in document.createElement('span')) { + var fn = $parse(attr['ngFileDropAvailable']); + $timeout(function() { + fn(scope); + }); + } + }; +} ]); + +angularFileUpload.directive('ngFileDrop', [ '$parse', '$timeout', function($parse, $timeout) { + return function(scope, elem, attr) { + if ('draggable' in document.createElement('span')) { + var cancel = null; + var fn = $parse(attr['ngFileDrop']); + elem[0].addEventListener("dragover", function(evt) { + $timeout.cancel(cancel); + evt.stopPropagation(); + evt.preventDefault(); + elem.addClass(attr['ngFileDragOverClass'] || "dragover"); + }, false); + elem[0].addEventListener("dragleave", function(evt) { + cancel = $timeout(function() { + elem.removeClass(attr['ngFileDragOverClass'] || "dragover"); + }); + }, false); + + var processing = 0; + function traverseFileTree(files, item) { + if (item.isDirectory) { + var dirReader = item.createReader(); + processing++; + dirReader.readEntries(function(entries) { + for (var i = 0; i < entries.length; i++) { + traverseFileTree(files, entries[i]); + } + processing--; + }); + } else { + processing++; + item.file(function(file) { + processing--; + files.push(file); + }); + } + } + + elem[0].addEventListener("drop", function(evt) { + evt.stopPropagation(); + evt.preventDefault(); + elem.removeClass(attr['ngFileDragOverClass'] || "dragover"); + var files = [], items = evt.dataTransfer.items; + if (items && items.length > 0 && items[0].webkitGetAsEntry) { + for (var i = 0; i < items.length; i++) { + traverseFileTree(files, items[i].webkitGetAsEntry()); + } + } else { + var fileList = evt.dataTransfer.files; + if (fileList != null) { + for (var i = 0; i < fileList.length; i++) { + files.push(fileList.item(i)); + } + } + } + (function callback(delay) { + $timeout(function() { + if (!processing) { + fn(scope, { + $files : files, + $event : evt + }); + } else { + callback(10); + } + }, delay || 0) + })(); + }, false); + } + }; +} ]); + +})();