parent.contains(descendant);
   return !!(descendant.compareDocumentPosition(parent) & 8);
    }
/**
 * Creates and returns element from html string
 * Uses innerHTML to create an element
 */
nt('div');
   div.innerHTML = html;
        var element = div.firstChild;
ild(element);
})();
element, styles){
  if (styles.opacity != null){
        if (typeof element.style.opacity != 'string' && typeof(element.filters) != 'undefined'){
         styles.filter = 'alpha(opacity=' + Math.round(100 * styles.opacity) + ')';
 qq.extend(element.style, styles);
.hasClass = function(element, name){
  return re.test(element.className);
.addClass = function(element, name){
      element.className += ' ' + name;
me){
 var re = new RegExp('(^| )' + name + '( |$)');
lace(re, ' ').replace(/^\s+|\s+$/g, "");
(element, text){
s
en.push(child);
ld = child.nextSibling;
    }
;
ngth;
var i = 0; i < len; i++){
qq.hasClass(candidates[i], className)){
   }
    }
qq.obj2url = function(obj, temp, prefixDone){
  var uristrings = [],
      prefix = '&',
ion(nextObj, i){
          var nextTemp = temp 
              ? (/\[\]$/.test(temp)) // prevent double-encoding
                   ? temp
 : temp+'['+i+']'
            : i;
         if ((nextTemp != 'undefined') && (i != 'undefined')) {  
                 (typeof nextObj === 'object') 
l(nextObj, nextTemp, true)
   : (Object.prototype.toString.call(nextObj) === '[object Function]')
nextTemp) + '=' + encodeURIComponent(nextObj())
       : encodeURIComponent(nextTemp) + '=' + encodeURIComponent(nextObj)                                                          
p) {
p)) ? (/\?$/.test(temp)) ? '' : '&' : '?';
.obj2url(obj));
== '[object Array]') && (typeof obj != 'undefined') ) {
t use a for-in-loop on an array (performance)
; i < len; ++i){
) && (typeof obj === "object")){
we will use for-in-loop
[i], i);
 uristrings.join(prefix)
ce(/%20/g, '+'); 
unction(o){
      // set to true to see the server response
     debug: false,
  multiple: true,
  allowedExtensions: [],               
// events
bmit
, fileName){},
ame, responseJSON){},
  typeError: "{file} has invalid extension. Only {extensions} are allowed.",
 large, maximum file size is {sizeLimit}.",
: "{file} is too small, minimum file size is {minSizeLimit}.",
s empty, please select files again without it.",
    onLeave: "The files are being uploaded, if you leave now the upload will be cancelled."            
t(message);
    qq.extend(this._options, o);
ogress: function(){
        return this._filesInProgress;         
eUploadButton: function(element){
return new qq.UploadButton({
qq.UploadHandlerXhr.isSupported(),
onChange: function(input){
put);
this,
  handlerClass;        
upported()){           
      handlerClass = 'UploadHandlerXhr';                        
      handlerClass = 'UploadHandlerForm';
qq[handlerClass]({
is._options.action,         
      maxConnections: this._options.maxConnections,   
ileName, loaded, total){                
eName, loaded, total);
          self._options.onProgress(id, fileName, loaded, total);                    
.onComplete(id, fileName, result);
fileName);
tions.onCancel(id, fileName);
});
ress: function(){
qq.attach(window, 'beforeunload', function(e){
urn;}
 = e || window.event;
/ for ie, ff
ges.onLeave;
;             
});        
ess: function(id, fileName, loaded, total){        
on(id, fileName, result){
this._filesInProgress--;                 
){
error);
unction(id, fileName){
f (this._handler instanceof qq.UploadHandlerXhr){                
loadFileList(input.files);                   
oadFile(input);                                    
}                      
i])){
 i++){
   this._uploadFile(files[i]);        
}        
ploadFile: function(fileContainer){      
leContainer);
if (this._options.onSubmit(id, fileName) !== false){
idateFile: function(file){
put            
   name = file.value.replace(/.*(\/|\\)/, "");
      // fix missing properties in Safari
ile.fileName : file.name;
    this._error('typeError', name);
 else if (size === 0){            
    this._error('emptyError', name);
lse;
options.sizeLimit && size > this._options.sizeLimit){            
return false;
(size && size < this._options.minSizeLimit){
izeError', name);
    return false;            
      return true;                
},
ror: function(code, fileName){
     var message = this._options.messages[code];        
e, replacement); }
     r('{file}', this._formatFileName(fileName));        
._options.allowedExtensions.join(', '));
_formatSize(this._options.sizeLimit));
s._options.minSizeLimit));
sage(message);                
unction(name){
ice(0, 19) + '...' + name.slice(-13);    
leName.replace(/.*[.]/, '').toLowerCase() : '';
this._options.allowedExtensions;
ue;}    
matSize: function(bytes){
;                                    
bytes, 0.1).toFixed(1) + ['kB', 'MB', 'GB', 'TB', 'PB', 'EB'][i];          
/**
 * Class that creates upload widget with drag-and-drop and file list
 * @inherits qq.FileUploaderBasic
 */
ments);
ditional options    
extend(this._options, {
 of qq-upload-list in template
        listElement: null,
></div>' +
            '<div class="qq-upload-button">Upload a file</div>' +
"></ul>' + 
     '</div>',
    // template for one item in file list
 +
ss="qq-upload-file"></span>' +
              '<span class="qq-upload-spinner"></span>' +
                '<span class="qq-upload-size"></span>' +
upload-cancel" href="#">Cancel</a>' +
            '</li>',        
 classes: {
    button: 'qq-upload-button',
upload-list',
   file: 'qq-upload-file',
    spinner: 'qq-upload-spinner',
pload-size',
      cancel: 'qq-upload-cancel',
list item when upload completes
            success: 'qq-upload-success',
.element;
options.template;        
Element = this._options.listElement || this._find(this._element, 'list');
ploadButton(this._find(this._element, 'button'));        
Basic Uploader
FileUploaderBasic.prototype);
     * Gets one of the elements listed in this._options.classes
     **/
unction(parent, type){                                
.classes[type])[0];        
pe);
ropArea = this._find(this._element, 'drop');                        
  var dz = new qq.UploadDropZone({
lf._classes.dropActive);
          e.stopPropagation();
                e.stopPropagation();
e){
a, self._classes.dropActive);  
    },
: function(e){
e.display = 'none';
.files);    
n(e){     
e.display = 'block';            
ion(e){
;            
t = document.elementFromPoint(e.clientX, e.clientY);
 ! relatedTarget || relatedTarget.nodeName == "HTML"){               
  });                
c.prototype._onSubmit.apply(this, arguments);
        this._addToList(id, fileName);  
rguments);
;
  var size = this._find(item, 'size');
e';
var text; 
 total * 100) + '% from ' + this._formatSize(total);
  qq.setText(size, text);         
lt){
qq.FileUploaderBasic.prototype._onComplete.apply(this, arguments);
d
qq.remove(this._find(item, 'cancel'));
qq.addClass(item, this._classes.success);    
lasses.fail);
this._options.fileTemplate);                
qqFileId = id;
ar fileElement = this._find(item, 'file');        
     qq.setText(fileElement, this._formatFileName(fileName));
    this._find(item, 'size').style.display = 'none';        
ment.appendChild(item);
id){
ent.firstChild;        
ly created list
  while (item){            
id) return item;            
        item = item.nextSibling;
},
     * delegate click event for cancel link 
     **/
on(){
      var self = this,
            list = this._listElement;            
tion(e){            
)){                
;
Node;
r.cancel(item.qqFileId);
 qq.remove(item);
}
= {
ave: function(e){},  
ng descendants   
ttachEvents();   
 function(e){
ances
(!qq.UploadDropZone.dropOutsideDisabled ){
        e.dataTransfer.dropEffect = 'none';
); 
    }           
Events: function(){
 = this;              
ion(e){
if (!self._isValidFileDrag(e)) return;
fer.effectAllowed;
t == 'move' || effect == 'linkMove'){
          e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)    
ropEffect = 'copy'; // for Chrome
            e.stopPropagation();
    if (!self._isValidFileDrag(e)) return;
         self._options.onEnter(e);
        });
f._element, 'dragleave', function(e){
sValidFileDrag(e)) return;
s.onLeave(e);
edTarget = document.elementFromPoint(e.clientX, e.clientY);                      
ng a mouse over a descendant
et)) return;
      self._options.onLeaveNotDescendants(e); 
    });
qq.attach(self._element, 'drop', function(e){
 return;
nDrop(e);
,
eck dt.types.contains in webkit, because it crashes safari 4            
  isWebkit = navigator.userAgent.indexOf("AppleWebKit") > -1;                        
 Safari 5
      // dt.types.contains check is for firefox            
        return dt && dt.effectAllowed != 'none' && 
Webkit && dt.types.contains && dt.types.contains('Files')));
 attribute to file input      
// name attribute of file input
verClass: 'qq-upload-button-hover',
_element = this._options.element;
nput
position: 'relative',
en',
n is in the right side
his._createInput();
put element */    
){
ns/recreates the file input */
t: function(){
            qq.remove(this._input);    
._options.focusClass);
s._input = this._createInput();
ut.setAttribute("name", this._options.name);
 position: 'absolute',
            // in Opera only 'browse' button
   // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
            fontSize: '118px',
adding: 0,
          cursor: 'pointer',
            opacity: 0
     });
     this._element.appendChild(input);
is;
ut, 'change', function(){
ange(input);
verClass);
  qq.attach(input, 'mouseout', function(){
ment, self._options.hoverClass);
    });
ut, 'focus', function(){
ement, self._options.focusClass);
      qq.attach(input, 'blur', function(){
nt, self._options.focusClass);
  // IE and Opera, unfortunately have 2 tab stops on file input
 // which is unacceptable in our case, disable keyboard access
is IE or Opera
input.setAttribute('tabIndex', "-1");
/**
 * Class for uploading files, uploading itself is handled by child classes
 */
ction(o){
'/upload.php',
s        
ed, total){},
nComplete: function(id, fileName, response){},
  onCancel: function(id, fileName){}
n queue
adHandlerAbstract.prototype = {
 if (this._options.debug && window.console) console.log('[uploader] ' + str);        
     * Adds file or file input to the queue
     * @returns id
     **/    
     * Sends the file identified by id and additional query params to the server
     */
params){
  var len = this._queue.push(id);
 qq.extend(copy, params);
opy;        
= this._options.maxConnections){               
 this._params[id]);
 }
     * Cancels file upload by id
     */
cel: function(id){
     * Cancells all uploads
     */
 for (var i=0; i<this._queue.length; i++){
s._queue[i]);
 }
     * Returns name of the file identified by id
     */
Name: function(id){},
     * Returns size of the file identified by id
     */          
Size: function(id){},
     * Returns id of files being uploaded or
     * waiting for their turn
     */
     * Actual upload method
     */
oad: function(id){},
     * Actual cancel method
     */
     * Removes element from queue, starts upload of next
     */
eue: function(id){
 = qq.indexOf(this._queue, id);
      this._queue.splice(i, 1);
     var max = this._options.maxConnections;
 && i < max){
         var nextId = this._queue[max-1];
is._params[nextId]);
    qq.UploadHandlerAbstract.apply(this, arguments);
d(qq.UploadHandlerForm.prototype, qq.UploadHandlerAbstract.prototype);
d(qq.UploadHandlerForm.prototype, {
me', 'qqfile');
er-iframe' + qq.getUniqueId();       
puts[id] = fileInput;
  // remove file input from DOM
ntNode){
ame: function(id){
th to normalize
        return this._inputs[id].value.replace(/.*(\/|\\)/, "");
n(id){
        var iframe = document.getElementById(id);
   // to cancel request set src to something else
 // we use src="javascript:false;" because it doesn't
, 'javascript:false;');
e(iframe);
    _upload: function(id, params){                        
 not added, or already uploaded or cancelled');
his.getName(id);
e(id);
form = this._createForm(iframe, params);
iframe, function(){                                 
self.log('iframe loaded');
frame);
te(id, fileName, response);
dequeue(id);
            delete self._inputs[id];
o fix busy state in FF3.6
){
        qq.remove(iframe);
 });
unction(iframe, callback){
tach(iframe, 'load', function(){
            // when we remove iframe from dom
 but in IE load
contentDocument.body &&
   iframe.contentDocument.body.innerHTML == "false"){
                // In Opera event is fired second time
 body.innerHTML changed from false
     // to server response approx. after 1 sec
          // when we upload file with iframe
         return;
     * Returns json object received by iframe from server.
     */
IframeContentJSON: function(iframe){
entDocument: iframe.contentWindow.document,
erHTML = " + doc.body.innerHTML);
            response = eval("(" + doc.body.innerHTML + ")");
      response = {};
 }        
 return response;
     * Creates iframe with unique name
     */
ode as the name attribute
indow
        // var iframe = document.createElement('iframe');
        var iframe = qq.toElement('<iframe src="javascript:false;" name="' + id + '" />');
oves ie6 prompt on https
        iframe.style.display = 'none';
appendChild(iframe);
 return iframe;
     * Creates form, that will be submitted to iframe
     */
);