").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+ // Otherwise use the full result
+ responseText );
+
+ }).complete( callback && function( jqXHR, status ) {
+ self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+ });
+ }
+
+ return this;
+};
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ){
+ jQuery.fn[ type ] = function( fn ){
+ return this.on( type, fn );
+ };
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+ jQuery[ method ] = function( url, data, callback, type ) {
+ // shift arguments if data argument was omitted
+ if ( jQuery.isFunction( data ) ) {
+ type = type || callback;
+ callback = data;
+ data = undefined;
+ }
+
+ return jQuery.ajax({
+ url: url,
+ type: method,
+ dataType: type,
+ data: data,
+ success: callback
+ });
+ };
+});
+
+jQuery.extend({
+
+ // Counter for holding the number of active queries
+ active: 0,
+
+ // Last-Modified header cache for next request
+ lastModified: {},
+ etag: {},
+
+ ajaxSettings: {
+ url: ajaxLocation,
+ type: "GET",
+ isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+ global: true,
+ processData: true,
+ async: true,
+ contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+ /*
+ timeout: 0,
+ data: null,
+ dataType: null,
+ username: null,
+ password: null,
+ cache: null,
+ throws: false,
+ traditional: false,
+ headers: {},
+ */
+
+ accepts: {
+ "*": allTypes,
+ text: "text/plain",
+ html: "text/html",
+ xml: "application/xml, text/xml",
+ json: "application/json, text/javascript"
+ },
+
+ contents: {
+ xml: /xml/,
+ html: /html/,
+ json: /json/
+ },
+
+ responseFields: {
+ xml: "responseXML",
+ text: "responseText"
+ },
+
+ // Data converters
+ // Keys separate source (or catchall "*") and destination types with a single space
+ converters: {
+
+ // Convert anything to text
+ "* text": window.String,
+
+ // Text to html (true = no transformation)
+ "text html": true,
+
+ // Evaluate text as a json expression
+ "text json": jQuery.parseJSON,
+
+ // Parse text as xml
+ "text xml": jQuery.parseXML
+ },
+
+ // For options that shouldn't be deep extended:
+ // you can add your own custom options here if
+ // and when you create one that shouldn't be
+ // deep extended (see ajaxExtend)
+ flatOptions: {
+ url: true,
+ context: true
+ }
+ },
+
+ // Creates a full fledged settings object into target
+ // with both ajaxSettings and settings fields.
+ // If target is omitted, writes into ajaxSettings.
+ ajaxSetup: function( target, settings ) {
+ return settings ?
+
+ // Building a settings object
+ ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+ // Extending ajaxSettings
+ ajaxExtend( jQuery.ajaxSettings, target );
+ },
+
+ ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+ ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+ // Main method
+ ajax: function( url, options ) {
+
+ // If url is an object, simulate pre-1.5 signature
+ if ( typeof url === "object" ) {
+ options = url;
+ url = undefined;
+ }
+
+ // Force options to be an object
+ options = options || {};
+
+ var // Cross-domain detection vars
+ parts,
+ // Loop variable
+ i,
+ // URL without anti-cache param
+ cacheURL,
+ // Response headers as string
+ responseHeadersString,
+ // timeout handle
+ timeoutTimer,
+
+ // To know if global events are to be dispatched
+ fireGlobals,
+
+ transport,
+ // Response headers
+ responseHeaders,
+ // Create the final options object
+ s = jQuery.ajaxSetup( {}, options ),
+ // Callbacks context
+ callbackContext = s.context || s,
+ // Context for global events is callbackContext if it is a DOM node or jQuery collection
+ globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+ jQuery( callbackContext ) :
+ jQuery.event,
+ // Deferreds
+ deferred = jQuery.Deferred(),
+ completeDeferred = jQuery.Callbacks("once memory"),
+ // Status-dependent callbacks
+ statusCode = s.statusCode || {},
+ // Headers (they are sent all at once)
+ requestHeaders = {},
+ requestHeadersNames = {},
+ // The jqXHR state
+ state = 0,
+ // Default abort message
+ strAbort = "canceled",
+ // Fake xhr
+ jqXHR = {
+ readyState: 0,
+
+ // Builds headers hashtable if needed
+ getResponseHeader: function( key ) {
+ var match;
+ if ( state === 2 ) {
+ if ( !responseHeaders ) {
+ responseHeaders = {};
+ while ( (match = rheaders.exec( responseHeadersString )) ) {
+ responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+ }
+ }
+ match = responseHeaders[ key.toLowerCase() ];
+ }
+ return match == null ? null : match;
+ },
+
+ // Raw string
+ getAllResponseHeaders: function() {
+ return state === 2 ? responseHeadersString : null;
+ },
+
+ // Caches the header
+ setRequestHeader: function( name, value ) {
+ var lname = name.toLowerCase();
+ if ( !state ) {
+ name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+ requestHeaders[ name ] = value;
+ }
+ return this;
+ },
+
+ // Overrides response content-type header
+ overrideMimeType: function( type ) {
+ if ( !state ) {
+ s.mimeType = type;
+ }
+ return this;
+ },
+
+ // Status-dependent callbacks
+ statusCode: function( map ) {
+ var code;
+ if ( map ) {
+ if ( state < 2 ) {
+ for ( code in map ) {
+ // Lazy-add the new callback in a way that preserves old ones
+ statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+ }
+ } else {
+ // Execute the appropriate callbacks
+ jqXHR.always( map[ jqXHR.status ] );
+ }
+ }
+ return this;
+ },
+
+ // Cancel the request
+ abort: function( statusText ) {
+ var finalText = statusText || strAbort;
+ if ( transport ) {
+ transport.abort( finalText );
+ }
+ done( 0, finalText );
+ return this;
+ }
+ };
+
+ // Attach deferreds
+ deferred.promise( jqXHR ).complete = completeDeferred.add;
+ jqXHR.success = jqXHR.done;
+ jqXHR.error = jqXHR.fail;
+
+ // Remove hash character (#7531: and string promotion)
+ // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
+ // Handle falsy url in the settings object (#10093: consistency with old signature)
+ // We also use the url parameter if available
+ s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+ // Alias method option to type as per ticket #12004
+ s.type = options.method || options.type || s.method || s.type;
+
+ // Extract dataTypes list
+ s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( core_rnotwhite ) || [""];
+
+ // A cross-domain request is in order when we have a protocol:host:port mismatch
+ if ( s.crossDomain == null ) {
+ parts = rurl.exec( s.url.toLowerCase() );
+ s.crossDomain = !!( parts &&
+ ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+ ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
+ ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
+ );
+ }
+
+ // Convert data if not already a string
+ if ( s.data && s.processData && typeof s.data !== "string" ) {
+ s.data = jQuery.param( s.data, s.traditional );
+ }
+
+ // Apply prefilters
+ inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+ // If request was aborted inside a prefilter, stop there
+ if ( state === 2 ) {
+ return jqXHR;
+ }
+
+ // We can fire global events as of now if asked to
+ fireGlobals = s.global;
+
+ // Watch for a new set of requests
+ if ( fireGlobals && jQuery.active++ === 0 ) {
+ jQuery.event.trigger("ajaxStart");
+ }
+
+ // Uppercase the type
+ s.type = s.type.toUpperCase();
+
+ // Determine if request has content
+ s.hasContent = !rnoContent.test( s.type );
+
+ // Save the URL in case we're toying with the If-Modified-Since
+ // and/or If-None-Match header later on
+ cacheURL = s.url;
+
+ // More options handling for requests with no content
+ if ( !s.hasContent ) {
+
+ // If data is available, append data to url
+ if ( s.data ) {
+ cacheURL = ( s.url += ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+ // #9682: remove data so that it's not used in an eventual retry
+ delete s.data;
+ }
+
+ // Add anti-cache in url if needed
+ if ( s.cache === false ) {
+ s.url = rts.test( cacheURL ) ?
+
+ // If there is already a '_' parameter, set its value
+ cacheURL.replace( rts, "$1_=" + ajax_nonce++ ) :
+
+ // Otherwise add one to the end
+ cacheURL + ( ajax_rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ajax_nonce++;
+ }
+ }
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ if ( jQuery.lastModified[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+ }
+ if ( jQuery.etag[ cacheURL ] ) {
+ jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+ }
+ }
+
+ // Set the correct header, if data is being sent
+ if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+ jqXHR.setRequestHeader( "Content-Type", s.contentType );
+ }
+
+ // Set the Accepts header for the server, depending on the dataType
+ jqXHR.setRequestHeader(
+ "Accept",
+ s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+ s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+ s.accepts[ "*" ]
+ );
+
+ // Check for headers option
+ for ( i in s.headers ) {
+ jqXHR.setRequestHeader( i, s.headers[ i ] );
+ }
+
+ // Allow custom headers/mimetypes and early abort
+ if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+ // Abort if not done already and return
+ return jqXHR.abort();
+ }
+
+ // aborting is no longer a cancellation
+ strAbort = "abort";
+
+ // Install callbacks on deferreds
+ for ( i in { success: 1, error: 1, complete: 1 } ) {
+ jqXHR[ i ]( s[ i ] );
+ }
+
+ // Get transport
+ transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+ // If no transport, we auto-abort
+ if ( !transport ) {
+ done( -1, "No Transport" );
+ } else {
+ jqXHR.readyState = 1;
+
+ // Send global event
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+ }
+ // Timeout
+ if ( s.async && s.timeout > 0 ) {
+ timeoutTimer = setTimeout(function() {
+ jqXHR.abort("timeout");
+ }, s.timeout );
+ }
+
+ try {
+ state = 1;
+ transport.send( requestHeaders, done );
+ } catch ( e ) {
+ // Propagate exception as error if not done
+ if ( state < 2 ) {
+ done( -1, e );
+ // Simply rethrow otherwise
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ // Callback for when everything is done
+ function done( status, nativeStatusText, responses, headers ) {
+ var isSuccess, success, error, response, modified,
+ statusText = nativeStatusText;
+
+ // Called once
+ if ( state === 2 ) {
+ return;
+ }
+
+ // State is "done" now
+ state = 2;
+
+ // Clear timeout if it exists
+ if ( timeoutTimer ) {
+ clearTimeout( timeoutTimer );
+ }
+
+ // Dereference transport for early garbage collection
+ // (no matter how long the jqXHR object will be used)
+ transport = undefined;
+
+ // Cache response headers
+ responseHeadersString = headers || "";
+
+ // Set readyState
+ jqXHR.readyState = status > 0 ? 4 : 0;
+
+ // Get response data
+ if ( responses ) {
+ response = ajaxHandleResponses( s, jqXHR, responses );
+ }
+
+ // If successful, handle type chaining
+ if ( status >= 200 && status < 300 || status === 304 ) {
+
+ // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+ if ( s.ifModified ) {
+ modified = jqXHR.getResponseHeader("Last-Modified");
+ if ( modified ) {
+ jQuery.lastModified[ cacheURL ] = modified;
+ }
+ modified = jqXHR.getResponseHeader("etag");
+ if ( modified ) {
+ jQuery.etag[ cacheURL ] = modified;
+ }
+ }
+
+ // if no content
+ if ( status === 204 ) {
+ isSuccess = true;
+ statusText = "nocontent";
+
+ // if not modified
+ } else if ( status === 304 ) {
+ isSuccess = true;
+ statusText = "notmodified";
+
+ // If we have data, let's convert it
+ } else {
+ isSuccess = ajaxConvert( s, response );
+ statusText = isSuccess.state;
+ success = isSuccess.data;
+ error = isSuccess.error;
+ isSuccess = !error;
+ }
+ } else {
+ // We extract error from statusText
+ // then normalize statusText and status for non-aborts
+ error = statusText;
+ if ( status || !statusText ) {
+ statusText = "error";
+ if ( status < 0 ) {
+ status = 0;
+ }
+ }
+ }
+
+ // Set data for the fake xhr object
+ jqXHR.status = status;
+ jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+ // Success/Error
+ if ( isSuccess ) {
+ deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+ } else {
+ deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+ }
+
+ // Status-dependent callbacks
+ jqXHR.statusCode( statusCode );
+ statusCode = undefined;
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+ [ jqXHR, s, isSuccess ? success : error ] );
+ }
+
+ // Complete
+ completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+ if ( fireGlobals ) {
+ globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+ // Handle the global AJAX counter
+ if ( !( --jQuery.active ) ) {
+ jQuery.event.trigger("ajaxStop");
+ }
+ }
+ }
+
+ return jqXHR;
+ },
+
+ getScript: function( url, callback ) {
+ return jQuery.get( url, undefined, callback, "script" );
+ },
+
+ getJSON: function( url, data, callback ) {
+ return jQuery.get( url, data, callback, "json" );
+ }
+});
+
+/* Handles responses to an ajax request:
+ * - sets all responseXXX fields accordingly
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+ var firstDataType, ct, finalDataType, type,
+ contents = s.contents,
+ dataTypes = s.dataTypes,
+ responseFields = s.responseFields;
+
+ // Fill responseXXX fields
+ for ( type in responseFields ) {
+ if ( type in responses ) {
+ jqXHR[ responseFields[type] ] = responses[ type ];
+ }
+ }
+
+ // Remove auto dataType and get content-type in the process
+ while( dataTypes[ 0 ] === "*" ) {
+ dataTypes.shift();
+ if ( ct === undefined ) {
+ ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+ }
+ }
+
+ // Check if we're dealing with a known content-type
+ if ( ct ) {
+ for ( type in contents ) {
+ if ( contents[ type ] && contents[ type ].test( ct ) ) {
+ dataTypes.unshift( type );
+ break;
+ }
+ }
+ }
+
+ // Check to see if we have a response for the expected dataType
+ if ( dataTypes[ 0 ] in responses ) {
+ finalDataType = dataTypes[ 0 ];
+ } else {
+ // Try convertible dataTypes
+ for ( type in responses ) {
+ if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+ finalDataType = type;
+ break;
+ }
+ if ( !firstDataType ) {
+ firstDataType = type;
+ }
+ }
+ // Or just use first one
+ finalDataType = finalDataType || firstDataType;
+ }
+
+ // If we found a dataType
+ // We add the dataType to the list if needed
+ // and return the corresponding response
+ if ( finalDataType ) {
+ if ( finalDataType !== dataTypes[ 0 ] ) {
+ dataTypes.unshift( finalDataType );
+ }
+ return responses[ finalDataType ];
+ }
+}
+
+// Chain conversions given the request and the original response
+function ajaxConvert( s, response ) {
+ var conv2, current, conv, tmp,
+ converters = {},
+ i = 0,
+ // Work with a copy of dataTypes in case we need to modify it for conversion
+ dataTypes = s.dataTypes.slice(),
+ prev = dataTypes[ 0 ];
+
+ // Apply the dataFilter if provided
+ if ( s.dataFilter ) {
+ response = s.dataFilter( response, s.dataType );
+ }
+
+ // Create converters map with lowercased keys
+ if ( dataTypes[ 1 ] ) {
+ for ( conv in s.converters ) {
+ converters[ conv.toLowerCase() ] = s.converters[ conv ];
+ }
+ }
+
+ // Convert to each sequential dataType, tolerating list modification
+ for ( ; (current = dataTypes[++i]); ) {
+
+ // There's only work to do if current dataType is non-auto
+ if ( current !== "*" ) {
+
+ // Convert response if prev dataType is non-auto and differs from current
+ if ( prev !== "*" && prev !== current ) {
+
+ // Seek a direct converter
+ conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+ // If none found, seek a pair
+ if ( !conv ) {
+ for ( conv2 in converters ) {
+
+ // If conv2 outputs current
+ tmp = conv2.split(" ");
+ if ( tmp[ 1 ] === current ) {
+
+ // If prev can be converted to accepted input
+ conv = converters[ prev + " " + tmp[ 0 ] ] ||
+ converters[ "* " + tmp[ 0 ] ];
+ if ( conv ) {
+ // Condense equivalence converters
+ if ( conv === true ) {
+ conv = converters[ conv2 ];
+
+ // Otherwise, insert the intermediate dataType
+ } else if ( converters[ conv2 ] !== true ) {
+ current = tmp[ 0 ];
+ dataTypes.splice( i--, 0, current );
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ // Apply converter (if not an equivalence)
+ if ( conv !== true ) {
+
+ // Unless errors are allowed to bubble, catch and return them
+ if ( conv && s["throws"] ) {
+ response = conv( response );
+ } else {
+ try {
+ response = conv( response );
+ } catch ( e ) {
+ return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+ }
+ }
+ }
+ }
+
+ // Update prev for next iteration
+ prev = current;
+ }
+ }
+
+ return { state: "success", data: response };
+}
+// Install script dataType
+jQuery.ajaxSetup({
+ accepts: {
+ script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+ },
+ contents: {
+ script: /(?:java|ecma)script/
+ },
+ converters: {
+ "text script": function( text ) {
+ jQuery.globalEval( text );
+ return text;
+ }
+ }
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter( "script", function( s ) {
+ if ( s.cache === undefined ) {
+ s.cache = false;
+ }
+ if ( s.crossDomain ) {
+ s.type = "GET";
+ s.global = false;
+ }
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function(s) {
+
+ // This transport only deals with cross domain requests
+ if ( s.crossDomain ) {
+
+ var script,
+ head = document.head || jQuery("head")[0] || document.documentElement;
+
+ return {
+
+ send: function( _, callback ) {
+
+ script = document.createElement("script");
+
+ script.async = true;
+
+ if ( s.scriptCharset ) {
+ script.charset = s.scriptCharset;
+ }
+
+ script.src = s.url;
+
+ // Attach handlers for all browsers
+ script.onload = script.onreadystatechange = function( _, isAbort ) {
+
+ if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
+ // Handle memory leak in IE
+ script.onload = script.onreadystatechange = null;
+
+ // Remove the script
+ if ( script.parentNode ) {
+ script.parentNode.removeChild( script );
+ }
+
+ // Dereference the script
+ script = null;
+
+ // Callback if not abort
+ if ( !isAbort ) {
+ callback( 200, "success" );
+ }
+ }
+ };
+
+ // Circumvent IE6 bugs with base elements (#2709 and #4378) by prepending
+ // Use native DOM manipulation to avoid our domManip AJAX trickery
+ head.insertBefore( script, head.firstChild );
+ },
+
+ abort: function() {
+ if ( script ) {
+ script.onload( undefined, true );
+ }
+ }
+ };
+ }
+});
+var oldCallbacks = [],
+ rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+ jsonp: "callback",
+ jsonpCallback: function() {
+ var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );
+ this[ callback ] = true;
+ return callback;
+ }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+ var callbackName, overwritten, responseContainer,
+ jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+ "url" :
+ typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+ );
+
+ // Handle iff the expected data type is "jsonp" or we have a parameter to set
+ if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+ // Get callback name, remembering preexisting value associated with it
+ callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+ s.jsonpCallback() :
+ s.jsonpCallback;
+
+ // Insert callback into url or form data
+ if ( jsonProp ) {
+ s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+ } else if ( s.jsonp !== false ) {
+ s.url += ( ajax_rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+ }
+
+ // Use data converter to retrieve json after script execution
+ s.converters["script json"] = function() {
+ if ( !responseContainer ) {
+ jQuery.error( callbackName + " was not called" );
+ }
+ return responseContainer[ 0 ];
+ };
+
+ // force json dataType
+ s.dataTypes[ 0 ] = "json";
+
+ // Install callback
+ overwritten = window[ callbackName ];
+ window[ callbackName ] = function() {
+ responseContainer = arguments;
+ };
+
+ // Clean-up function (fires after converters)
+ jqXHR.always(function() {
+ // Restore preexisting value
+ window[ callbackName ] = overwritten;
+
+ // Save back as free
+ if ( s[ callbackName ] ) {
+ // make sure that re-using the options doesn't screw things around
+ s.jsonpCallback = originalSettings.jsonpCallback;
+
+ // save the callback name for future use
+ oldCallbacks.push( callbackName );
+ }
+
+ // Call if it was a function and we have a response
+ if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+ overwritten( responseContainer[ 0 ] );
+ }
+
+ responseContainer = overwritten = undefined;
+ });
+
+ // Delegate to script
+ return "script";
+ }
+});
+var xhrCallbacks, xhrSupported,
+ xhrId = 0,
+ // #5280: Internet Explorer will keep connections alive if we don't abort on unload
+ xhrOnUnloadAbort = window.ActiveXObject && function() {
+ // Abort all pending requests
+ var key;
+ for ( key in xhrCallbacks ) {
+ xhrCallbacks[ key ]( undefined, true );
+ }
+ };
+
+// Functions to create xhrs
+function createStandardXHR() {
+ try {
+ return new window.XMLHttpRequest();
+ } catch( e ) {}
+}
+
+function createActiveXHR() {
+ try {
+ return new window.ActiveXObject("Microsoft.XMLHTTP");
+ } catch( e ) {}
+}
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+ /* Microsoft failed to properly
+ * implement the XMLHttpRequest in IE7 (can't request local files),
+ * so we use the ActiveXObject when it is available
+ * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+ * we need a fallback.
+ */
+ function() {
+ return !this.isLocal && createStandardXHR() || createActiveXHR();
+ } :
+ // For all other browsers, use the standard XMLHttpRequest object
+ createStandardXHR;
+
+// Determine support properties
+xhrSupported = jQuery.ajaxSettings.xhr();
+jQuery.support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+xhrSupported = jQuery.support.ajax = !!xhrSupported;
+
+// Create transport if the browser can provide an xhr
+if ( xhrSupported ) {
+
+ jQuery.ajaxTransport(function( s ) {
+ // Cross domain only allowed if supported through XMLHttpRequest
+ if ( !s.crossDomain || jQuery.support.cors ) {
+
+ var callback;
+
+ return {
+ send: function( headers, complete ) {
+
+ // Get a new xhr
+ var handle, i,
+ xhr = s.xhr();
+
+ // Open the socket
+ // Passing null username, generates a login popup on Opera (#2865)
+ if ( s.username ) {
+ xhr.open( s.type, s.url, s.async, s.username, s.password );
+ } else {
+ xhr.open( s.type, s.url, s.async );
+ }
+
+ // Apply custom fields if provided
+ if ( s.xhrFields ) {
+ for ( i in s.xhrFields ) {
+ xhr[ i ] = s.xhrFields[ i ];
+ }
+ }
+
+ // Override mime type if needed
+ if ( s.mimeType && xhr.overrideMimeType ) {
+ xhr.overrideMimeType( s.mimeType );
+ }
+
+ // X-Requested-With header
+ // For cross-domain requests, seeing as conditions for a preflight are
+ // akin to a jigsaw puzzle, we simply never set it to be sure.
+ // (it can always be set on a per-request basis or even using ajaxSetup)
+ // For same-domain requests, won't change header if already provided.
+ if ( !s.crossDomain && !headers["X-Requested-With"] ) {
+ headers["X-Requested-With"] = "XMLHttpRequest";
+ }
+
+ // Need an extra try/catch for cross domain requests in Firefox 3
+ try {
+ for ( i in headers ) {
+ xhr.setRequestHeader( i, headers[ i ] );
+ }
+ } catch( err ) {}
+
+ // Do send the request
+ // This may raise an exception which is actually
+ // handled in jQuery.ajax (so no try/catch here)
+ xhr.send( ( s.hasContent && s.data ) || null );
+
+ // Listener
+ callback = function( _, isAbort ) {
+ var status, responseHeaders, statusText, responses;
+
+ // Firefox throws exceptions when accessing properties
+ // of an xhr when a network error occurred
+ // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
+ try {
+
+ // Was never called and is aborted or complete
+ if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+ // Only called once
+ callback = undefined;
+
+ // Do not keep as active anymore
+ if ( handle ) {
+ xhr.onreadystatechange = jQuery.noop;
+ if ( xhrOnUnloadAbort ) {
+ delete xhrCallbacks[ handle ];
+ }
+ }
+
+ // If it's an abort
+ if ( isAbort ) {
+ // Abort it manually if needed
+ if ( xhr.readyState !== 4 ) {
+ xhr.abort();
+ }
+ } else {
+ responses = {};
+ status = xhr.status;
+ responseHeaders = xhr.getAllResponseHeaders();
+
+ // When requesting binary data, IE6-9 will throw an exception
+ // on any attempt to access responseText (#11426)
+ if ( typeof xhr.responseText === "string" ) {
+ responses.text = xhr.responseText;
+ }
+
+ // Firefox throws an exception when accessing
+ // statusText for faulty cross-domain requests
+ try {
+ statusText = xhr.statusText;
+ } catch( e ) {
+ // We normalize with Webkit giving an empty statusText
+ statusText = "";
+ }
+
+ // Filter status for non standard behaviors
+
+ // If the request is local and we have data: assume a success
+ // (success with no data won't get notified, that's the best we
+ // can do given current implementations)
+ if ( !status && s.isLocal && !s.crossDomain ) {
+ status = responses.text ? 200 : 404;
+ // IE - #1450: sometimes returns 1223 when it should be 204
+ } else if ( status === 1223 ) {
+ status = 204;
+ }
+ }
+ }
+ } catch( firefoxAccessException ) {
+ if ( !isAbort ) {
+ complete( -1, firefoxAccessException );
+ }
+ }
+
+ // Call complete if needed
+ if ( responses ) {
+ complete( status, statusText, responses, responseHeaders );
+ }
+ };
+
+ if ( !s.async ) {
+ // if we're in sync mode we fire the callback
+ callback();
+ } else if ( xhr.readyState === 4 ) {
+ // (IE6 & IE7) if it's in cache and has been
+ // retrieved directly we need to fire the callback
+ setTimeout( callback );
+ } else {
+ handle = ++xhrId;
+ if ( xhrOnUnloadAbort ) {
+ // Create the active xhrs callbacks list if needed
+ // and attach the unload handler
+ if ( !xhrCallbacks ) {
+ xhrCallbacks = {};
+ jQuery( window ).unload( xhrOnUnloadAbort );
+ }
+ // Add to list of active xhrs callbacks
+ xhrCallbacks[ handle ] = callback;
+ }
+ xhr.onreadystatechange = callback;
+ }
+ },
+
+ abort: function() {
+ if ( callback ) {
+ callback( undefined, true );
+ }
+ }
+ };
+ }
+ });
+}
+var fxNow, timerId,
+ rfxtypes = /^(?:toggle|show|hide)$/,
+ rfxnum = new RegExp( "^(?:([+-])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
+ rrun = /queueHooks$/,
+ animationPrefilters = [ defaultPrefilter ],
+ tweeners = {
+ "*": [function( prop, value ) {
+ var end, unit,
+ tween = this.createTween( prop, value ),
+ parts = rfxnum.exec( value ),
+ target = tween.cur(),
+ start = +target || 0,
+ scale = 1,
+ maxIterations = 20;
+
+ if ( parts ) {
+ end = +parts[2];
+ unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+
+ // We need to compute starting value
+ if ( unit !== "px" && start ) {
+ // Iteratively approximate from a nonzero starting point
+ // Prefer the current property, because this process will be trivial if it uses the same units
+ // Fallback to end or a simple constant
+ start = jQuery.css( tween.elem, prop, true ) || end || 1;
+
+ do {
+ // If previous iteration zeroed out, double until we get *something*
+ // Use a string for doubling factor so we don't accidentally see scale as unchanged below
+ scale = scale || ".5";
+
+ // Adjust and apply
+ start = start / scale;
+ jQuery.style( tween.elem, prop, start + unit );
+
+ // Update scale, tolerating zero or NaN from tween.cur()
+ // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+ } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+ }
+
+ tween.unit = unit;
+ tween.start = start;
+ // If a +=/-= token was provided, we're doing a relative animation
+ tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
+ }
+ return tween;
+ }]
+ };
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+ setTimeout(function() {
+ fxNow = undefined;
+ });
+ return ( fxNow = jQuery.now() );
+}
+
+function createTweens( animation, props ) {
+ jQuery.each( props, function( prop, value ) {
+ var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+ index = 0,
+ length = collection.length;
+ for ( ; index < length; index++ ) {
+ if ( collection[ index ].call( animation, prop, value ) ) {
+
+ // we're done with this property
+ return;
+ }
+ }
+ });
+}
+
+function Animation( elem, properties, options ) {
+ var result,
+ stopped,
+ index = 0,
+ length = animationPrefilters.length,
+ deferred = jQuery.Deferred().always( function() {
+ // don't match elem in the :animated selector
+ delete tick.elem;
+ }),
+ tick = function() {
+ if ( stopped ) {
+ return false;
+ }
+ var currentTime = fxNow || createFxNow(),
+ remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+ // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+ temp = remaining / animation.duration || 0,
+ percent = 1 - temp,
+ index = 0,
+ length = animation.tweens.length;
+
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( percent );
+ }
+
+ deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+ if ( percent < 1 && length ) {
+ return remaining;
+ } else {
+ deferred.resolveWith( elem, [ animation ] );
+ return false;
+ }
+ },
+ animation = deferred.promise({
+ elem: elem,
+ props: jQuery.extend( {}, properties ),
+ opts: jQuery.extend( true, { specialEasing: {} }, options ),
+ originalProperties: properties,
+ originalOptions: options,
+ startTime: fxNow || createFxNow(),
+ duration: options.duration,
+ tweens: [],
+ createTween: function( prop, end ) {
+ var tween = jQuery.Tween( elem, animation.opts, prop, end,
+ animation.opts.specialEasing[ prop ] || animation.opts.easing );
+ animation.tweens.push( tween );
+ return tween;
+ },
+ stop: function( gotoEnd ) {
+ var index = 0,
+ // if we are going to the end, we want to run all the tweens
+ // otherwise we skip this part
+ length = gotoEnd ? animation.tweens.length : 0;
+ if ( stopped ) {
+ return this;
+ }
+ stopped = true;
+ for ( ; index < length ; index++ ) {
+ animation.tweens[ index ].run( 1 );
+ }
+
+ // resolve when we played the last frame
+ // otherwise, reject
+ if ( gotoEnd ) {
+ deferred.resolveWith( elem, [ animation, gotoEnd ] );
+ } else {
+ deferred.rejectWith( elem, [ animation, gotoEnd ] );
+ }
+ return this;
+ }
+ }),
+ props = animation.props;
+
+ propFilter( props, animation.opts.specialEasing );
+
+ for ( ; index < length ; index++ ) {
+ result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+ if ( result ) {
+ return result;
+ }
+ }
+
+ createTweens( animation, props );
+
+ if ( jQuery.isFunction( animation.opts.start ) ) {
+ animation.opts.start.call( elem, animation );
+ }
+
+ jQuery.fx.timer(
+ jQuery.extend( tick, {
+ elem: elem,
+ anim: animation,
+ queue: animation.opts.queue
+ })
+ );
+
+ // attach callbacks from options
+ return animation.progress( animation.opts.progress )
+ .done( animation.opts.done, animation.opts.complete )
+ .fail( animation.opts.fail )
+ .always( animation.opts.always );
+}
+
+function propFilter( props, specialEasing ) {
+ var value, name, index, easing, hooks;
+
+ // camelCase, specialEasing and expand cssHook pass
+ for ( index in props ) {
+ name = jQuery.camelCase( index );
+ easing = specialEasing[ name ];
+ value = props[ index ];
+ if ( jQuery.isArray( value ) ) {
+ easing = value[ 1 ];
+ value = props[ index ] = value[ 0 ];
+ }
+
+ if ( index !== name ) {
+ props[ name ] = value;
+ delete props[ index ];
+ }
+
+ hooks = jQuery.cssHooks[ name ];
+ if ( hooks && "expand" in hooks ) {
+ value = hooks.expand( value );
+ delete props[ name ];
+
+ // not quite $.extend, this wont overwrite keys already present.
+ // also - reusing 'index' from above because we have the correct "name"
+ for ( index in value ) {
+ if ( !( index in props ) ) {
+ props[ index ] = value[ index ];
+ specialEasing[ index ] = easing;
+ }
+ }
+ } else {
+ specialEasing[ name ] = easing;
+ }
+ }
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+ tweener: function( props, callback ) {
+ if ( jQuery.isFunction( props ) ) {
+ callback = props;
+ props = [ "*" ];
+ } else {
+ props = props.split(" ");
+ }
+
+ var prop,
+ index = 0,
+ length = props.length;
+
+ for ( ; index < length ; index++ ) {
+ prop = props[ index ];
+ tweeners[ prop ] = tweeners[ prop ] || [];
+ tweeners[ prop ].unshift( callback );
+ }
+ },
+
+ prefilter: function( callback, prepend ) {
+ if ( prepend ) {
+ animationPrefilters.unshift( callback );
+ } else {
+ animationPrefilters.push( callback );
+ }
+ }
+});
+
+function defaultPrefilter( elem, props, opts ) {
+ /*jshint validthis:true */
+ var prop, index, length,
+ value, dataShow, toggle,
+ tween, hooks, oldfire,
+ anim = this,
+ style = elem.style,
+ orig = {},
+ handled = [],
+ hidden = elem.nodeType && isHidden( elem );
+
+ // handle queue: false promises
+ if ( !opts.queue ) {
+ hooks = jQuery._queueHooks( elem, "fx" );
+ if ( hooks.unqueued == null ) {
+ hooks.unqueued = 0;
+ oldfire = hooks.empty.fire;
+ hooks.empty.fire = function() {
+ if ( !hooks.unqueued ) {
+ oldfire();
+ }
+ };
+ }
+ hooks.unqueued++;
+
+ anim.always(function() {
+ // doing this makes sure that the complete handler will be called
+ // before this completes
+ anim.always(function() {
+ hooks.unqueued--;
+ if ( !jQuery.queue( elem, "fx" ).length ) {
+ hooks.empty.fire();
+ }
+ });
+ });
+ }
+
+ // height/width overflow pass
+ if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+ // Make sure that nothing sneaks out
+ // Record all 3 overflow attributes because IE does not
+ // change the overflow attribute when overflowX and
+ // overflowY are set to the same value
+ opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+ // Set display property to inline-block for height/width
+ // animations on inline elements that are having width/height animated
+ if ( jQuery.css( elem, "display" ) === "inline" &&
+ jQuery.css( elem, "float" ) === "none" ) {
+
+ // inline-level elements accept inline-block;
+ // block-level elements need to be inline with layout
+ if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
+ style.display = "inline-block";
+
+ } else {
+ style.zoom = 1;
+ }
+ }
+ }
+
+ if ( opts.overflow ) {
+ style.overflow = "hidden";
+ if ( !jQuery.support.shrinkWrapBlocks ) {
+ anim.always(function() {
+ style.overflow = opts.overflow[ 0 ];
+ style.overflowX = opts.overflow[ 1 ];
+ style.overflowY = opts.overflow[ 2 ];
+ });
+ }
+ }
+
+
+ // show/hide pass
+ for ( index in props ) {
+ value = props[ index ];
+ if ( rfxtypes.exec( value ) ) {
+ delete props[ index ];
+ toggle = toggle || value === "toggle";
+ if ( value === ( hidden ? "hide" : "show" ) ) {
+ continue;
+ }
+ handled.push( index );
+ }
+ }
+
+ length = handled.length;
+ if ( length ) {
+ dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
+ if ( "hidden" in dataShow ) {
+ hidden = dataShow.hidden;
+ }
+
+ // store state if its toggle - enables .stop().toggle() to "reverse"
+ if ( toggle ) {
+ dataShow.hidden = !hidden;
+ }
+ if ( hidden ) {
+ jQuery( elem ).show();
+ } else {
+ anim.done(function() {
+ jQuery( elem ).hide();
+ });
+ }
+ anim.done(function() {
+ var prop;
+ jQuery._removeData( elem, "fxshow" );
+ for ( prop in orig ) {
+ jQuery.style( elem, prop, orig[ prop ] );
+ }
+ });
+ for ( index = 0 ; index < length ; index++ ) {
+ prop = handled[ index ];
+ tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
+ orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
+
+ if ( !( prop in dataShow ) ) {
+ dataShow[ prop ] = tween.start;
+ if ( hidden ) {
+ tween.end = tween.start;
+ tween.start = prop === "width" || prop === "height" ? 1 : 0;
+ }
+ }
+ }
+ }
+}
+
+function Tween( elem, options, prop, end, easing ) {
+ return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+ constructor: Tween,
+ init: function( elem, options, prop, end, easing, unit ) {
+ this.elem = elem;
+ this.prop = prop;
+ this.easing = easing || "swing";
+ this.options = options;
+ this.start = this.now = this.cur();
+ this.end = end;
+ this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+ },
+ cur: function() {
+ var hooks = Tween.propHooks[ this.prop ];
+
+ return hooks && hooks.get ?
+ hooks.get( this ) :
+ Tween.propHooks._default.get( this );
+ },
+ run: function( percent ) {
+ var eased,
+ hooks = Tween.propHooks[ this.prop ];
+
+ if ( this.options.duration ) {
+ this.pos = eased = jQuery.easing[ this.easing ](
+ percent, this.options.duration * percent, 0, 1, this.options.duration
+ );
+ } else {
+ this.pos = eased = percent;
+ }
+ this.now = ( this.end - this.start ) * eased + this.start;
+
+ if ( this.options.step ) {
+ this.options.step.call( this.elem, this.now, this );
+ }
+
+ if ( hooks && hooks.set ) {
+ hooks.set( this );
+ } else {
+ Tween.propHooks._default.set( this );
+ }
+ return this;
+ }
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+ _default: {
+ get: function( tween ) {
+ var result;
+
+ if ( tween.elem[ tween.prop ] != null &&
+ (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+ return tween.elem[ tween.prop ];
+ }
+
+ // passing an empty string as a 3rd parameter to .css will automatically
+ // attempt a parseFloat and fallback to a string if the parse fails
+ // so, simple values such as "10px" are parsed to Float.
+ // complex values such as "rotate(1rad)" are returned as is.
+ result = jQuery.css( tween.elem, tween.prop, "" );
+ // Empty strings, null, undefined and "auto" are converted to 0.
+ return !result || result === "auto" ? 0 : result;
+ },
+ set: function( tween ) {
+ // use step hook for back compat - use cssHook if its there - use .style if its
+ // available and use plain properties where available
+ if ( jQuery.fx.step[ tween.prop ] ) {
+ jQuery.fx.step[ tween.prop ]( tween );
+ } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+ jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+ } else {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+ }
+};
+
+// Remove in 2.0 - this supports IE8's panic based approach
+// to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+ set: function( tween ) {
+ if ( tween.elem.nodeType && tween.elem.parentNode ) {
+ tween.elem[ tween.prop ] = tween.now;
+ }
+ }
+};
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+ var cssFn = jQuery.fn[ name ];
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return speed == null || typeof speed === "boolean" ?
+ cssFn.apply( this, arguments ) :
+ this.animate( genFx( name, true ), speed, easing, callback );
+ };
+});
+
+jQuery.fn.extend({
+ fadeTo: function( speed, to, easing, callback ) {
+
+ // show any hidden elements after setting opacity to 0
+ return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+ // animate to the value specified
+ .end().animate({ opacity: to }, speed, easing, callback );
+ },
+ animate: function( prop, speed, easing, callback ) {
+ var empty = jQuery.isEmptyObject( prop ),
+ optall = jQuery.speed( speed, easing, callback ),
+ doAnimation = function() {
+ // Operate on a copy of prop so per-property easing won't be lost
+ var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+ doAnimation.finish = function() {
+ anim.stop( true );
+ };
+ // Empty animations, or finishing resolves immediately
+ if ( empty || jQuery._data( this, "finish" ) ) {
+ anim.stop( true );
+ }
+ };
+ doAnimation.finish = doAnimation;
+
+ return empty || optall.queue === false ?
+ this.each( doAnimation ) :
+ this.queue( optall.queue, doAnimation );
+ },
+ stop: function( type, clearQueue, gotoEnd ) {
+ var stopQueue = function( hooks ) {
+ var stop = hooks.stop;
+ delete hooks.stop;
+ stop( gotoEnd );
+ };
+
+ if ( typeof type !== "string" ) {
+ gotoEnd = clearQueue;
+ clearQueue = type;
+ type = undefined;
+ }
+ if ( clearQueue && type !== false ) {
+ this.queue( type || "fx", [] );
+ }
+
+ return this.each(function() {
+ var dequeue = true,
+ index = type != null && type + "queueHooks",
+ timers = jQuery.timers,
+ data = jQuery._data( this );
+
+ if ( index ) {
+ if ( data[ index ] && data[ index ].stop ) {
+ stopQueue( data[ index ] );
+ }
+ } else {
+ for ( index in data ) {
+ if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+ stopQueue( data[ index ] );
+ }
+ }
+ }
+
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+ timers[ index ].anim.stop( gotoEnd );
+ dequeue = false;
+ timers.splice( index, 1 );
+ }
+ }
+
+ // start the next in the queue if the last step wasn't forced
+ // timers currently will call their complete callbacks, which will dequeue
+ // but only if they were gotoEnd
+ if ( dequeue || !gotoEnd ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ finish: function( type ) {
+ if ( type !== false ) {
+ type = type || "fx";
+ }
+ return this.each(function() {
+ var index,
+ data = jQuery._data( this ),
+ queue = data[ type + "queue" ],
+ hooks = data[ type + "queueHooks" ],
+ timers = jQuery.timers,
+ length = queue ? queue.length : 0;
+
+ // enable finishing flag on private data
+ data.finish = true;
+
+ // empty the queue first
+ jQuery.queue( this, type, [] );
+
+ if ( hooks && hooks.cur && hooks.cur.finish ) {
+ hooks.cur.finish.call( this );
+ }
+
+ // look for any active animations, and finish them
+ for ( index = timers.length; index--; ) {
+ if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+ timers[ index ].anim.stop( true );
+ timers.splice( index, 1 );
+ }
+ }
+
+ // look for any animations in the old queue and finish them
+ for ( index = 0; index < length; index++ ) {
+ if ( queue[ index ] && queue[ index ].finish ) {
+ queue[ index ].finish.call( this );
+ }
+ }
+
+ // turn off finishing flag
+ delete data.finish;
+ });
+ }
+});
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+ var which,
+ attrs = { height: type },
+ i = 0;
+
+ // if we include width, step value is 1 to do all cssExpand values,
+ // if we don't include width, step value is 2 to skip over Left and Right
+ includeWidth = includeWidth? 1 : 0;
+ for( ; i < 4 ; i += 2 - includeWidth ) {
+ which = cssExpand[ i ];
+ attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+ }
+
+ if ( includeWidth ) {
+ attrs.opacity = attrs.width = type;
+ }
+
+ return attrs;
+}
+
+// Generate shortcuts for custom animations
+jQuery.each({
+ slideDown: genFx("show"),
+ slideUp: genFx("hide"),
+ slideToggle: genFx("toggle"),
+ fadeIn: { opacity: "show" },
+ fadeOut: { opacity: "hide" },
+ fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+ jQuery.fn[ name ] = function( speed, easing, callback ) {
+ return this.animate( props, speed, easing, callback );
+ };
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+ var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+ complete: fn || !fn && easing ||
+ jQuery.isFunction( speed ) && speed,
+ duration: speed,
+ easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+ };
+
+ opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+ opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+ // normalize opt.queue - true/undefined/null -> "fx"
+ if ( opt.queue == null || opt.queue === true ) {
+ opt.queue = "fx";
+ }
+
+ // Queueing
+ opt.old = opt.complete;
+
+ opt.complete = function() {
+ if ( jQuery.isFunction( opt.old ) ) {
+ opt.old.call( this );
+ }
+
+ if ( opt.queue ) {
+ jQuery.dequeue( this, opt.queue );
+ }
+ };
+
+ return opt;
+};
+
+jQuery.easing = {
+ linear: function( p ) {
+ return p;
+ },
+ swing: function( p ) {
+ return 0.5 - Math.cos( p*Math.PI ) / 2;
+ }
+};
+
+jQuery.timers = [];
+jQuery.fx = Tween.prototype.init;
+jQuery.fx.tick = function() {
+ var timer,
+ timers = jQuery.timers,
+ i = 0;
+
+ fxNow = jQuery.now();
+
+ for ( ; i < timers.length; i++ ) {
+ timer = timers[ i ];
+ // Checks the timer has not already been removed
+ if ( !timer() && timers[ i ] === timer ) {
+ timers.splice( i--, 1 );
+ }
+ }
+
+ if ( !timers.length ) {
+ jQuery.fx.stop();
+ }
+ fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+ if ( timer() && jQuery.timers.push( timer ) ) {
+ jQuery.fx.start();
+ }
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+ if ( !timerId ) {
+ timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+ }
+};
+
+jQuery.fx.stop = function() {
+ clearInterval( timerId );
+ timerId = null;
+};
+
+jQuery.fx.speeds = {
+ slow: 600,
+ fast: 200,
+ // Default speed
+ _default: 400
+};
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+if ( jQuery.expr && jQuery.expr.filters ) {
+ jQuery.expr.filters.animated = function( elem ) {
+ return jQuery.grep(jQuery.timers, function( fn ) {
+ return elem === fn.elem;
+ }).length;
+ };
+}
+jQuery.fn.offset = function( options ) {
+ if ( arguments.length ) {
+ return options === undefined ?
+ this :
+ this.each(function( i ) {
+ jQuery.offset.setOffset( this, options, i );
+ });
+ }
+
+ var docElem, win,
+ box = { top: 0, left: 0 },
+ elem = this[ 0 ],
+ doc = elem && elem.ownerDocument;
+
+ if ( !doc ) {
+ return;
+ }
+
+ docElem = doc.documentElement;
+
+ // Make sure it's not a disconnected DOM node
+ if ( !jQuery.contains( docElem, elem ) ) {
+ return box;
+ }
+
+ // If we don't have gBCR, just use 0,0 rather than error
+ // BlackBerry 5, iOS 3 (original iPhone)
+ if ( typeof elem.getBoundingClientRect !== core_strundefined ) {
+ box = elem.getBoundingClientRect();
+ }
+ win = getWindow( doc );
+ return {
+ top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
+ left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
+ };
+};
+
+jQuery.offset = {
+
+ setOffset: function( elem, options, i ) {
+ var position = jQuery.css( elem, "position" );
+
+ // set position first, in-case top/left are set even on static elem
+ if ( position === "static" ) {
+ elem.style.position = "relative";
+ }
+
+ var curElem = jQuery( elem ),
+ curOffset = curElem.offset(),
+ curCSSTop = jQuery.css( elem, "top" ),
+ curCSSLeft = jQuery.css( elem, "left" ),
+ calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
+ props = {}, curPosition = {}, curTop, curLeft;
+
+ // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+ if ( calculatePosition ) {
+ curPosition = curElem.position();
+ curTop = curPosition.top;
+ curLeft = curPosition.left;
+ } else {
+ curTop = parseFloat( curCSSTop ) || 0;
+ curLeft = parseFloat( curCSSLeft ) || 0;
+ }
+
+ if ( jQuery.isFunction( options ) ) {
+ options = options.call( elem, i, curOffset );
+ }
+
+ if ( options.top != null ) {
+ props.top = ( options.top - curOffset.top ) + curTop;
+ }
+ if ( options.left != null ) {
+ props.left = ( options.left - curOffset.left ) + curLeft;
+ }
+
+ if ( "using" in options ) {
+ options.using.call( elem, props );
+ } else {
+ curElem.css( props );
+ }
+ }
+};
+
+
+jQuery.fn.extend({
+
+ position: function() {
+ if ( !this[ 0 ] ) {
+ return;
+ }
+
+ var offsetParent, offset,
+ parentOffset = { top: 0, left: 0 },
+ elem = this[ 0 ];
+
+ // fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
+ if ( jQuery.css( elem, "position" ) === "fixed" ) {
+ // we assume that getBoundingClientRect is available when computed position is fixed
+ offset = elem.getBoundingClientRect();
+ } else {
+ // Get *real* offsetParent
+ offsetParent = this.offsetParent();
+
+ // Get correct offsets
+ offset = this.offset();
+ if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+ parentOffset = offsetParent.offset();
+ }
+
+ // Add offsetParent borders
+ parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+ parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+ }
+
+ // Subtract parent offsets and element margins
+ // note: when an element has margin: auto the offsetLeft and marginLeft
+ // are the same in Safari causing offset.left to incorrectly be 0
+ return {
+ top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+ left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true)
+ };
+ },
+
+ offsetParent: function() {
+ return this.map(function() {
+ var offsetParent = this.offsetParent || document.documentElement;
+ while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position") === "static" ) ) {
+ offsetParent = offsetParent.offsetParent;
+ }
+ return offsetParent || document.documentElement;
+ });
+ }
+});
+
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
+ var top = /Y/.test( prop );
+
+ jQuery.fn[ method ] = function( val ) {
+ return jQuery.access( this, function( elem, method, val ) {
+ var win = getWindow( elem );
+
+ if ( val === undefined ) {
+ return win ? (prop in win) ? win[ prop ] :
+ win.document.documentElement[ method ] :
+ elem[ method ];
+ }
+
+ if ( win ) {
+ win.scrollTo(
+ !top ? val : jQuery( win ).scrollLeft(),
+ top ? val : jQuery( win ).scrollTop()
+ );
+
+ } else {
+ elem[ method ] = val;
+ }
+ }, method, val, arguments.length, null );
+ };
+});
+
+function getWindow( elem ) {
+ return jQuery.isWindow( elem ) ?
+ elem :
+ elem.nodeType === 9 ?
+ elem.defaultView || elem.parentWindow :
+ false;
+}
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+ jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+ // margin is only for outerHeight, outerWidth
+ jQuery.fn[ funcName ] = function( margin, value ) {
+ var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+ extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+ return jQuery.access( this, function( elem, type, value ) {
+ var doc;
+
+ if ( jQuery.isWindow( elem ) ) {
+ // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+ // isn't a whole lot we can do. See pull request at this URL for discussion:
+ // https://github.com/jquery/jquery/pull/764
+ return elem.document.documentElement[ "client" + name ];
+ }
+
+ // Get document width or height
+ if ( elem.nodeType === 9 ) {
+ doc = elem.documentElement;
+
+ // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
+ // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
+ return Math.max(
+ elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+ elem.body[ "offset" + name ], doc[ "offset" + name ],
+ doc[ "client" + name ]
+ );
+ }
+
+ return value === undefined ?
+ // Get width or height on the element, requesting but not forcing parseFloat
+ jQuery.css( elem, type, extra ) :
+
+ // Set width or height on the element
+ jQuery.style( elem, type, value, extra );
+ }, type, chainable ? margin : undefined, chainable, null );
+ };
+ });
+});
+// Limit scope pollution from any deprecated API
+// (function() {
+
+// })();
+// Expose jQuery to the global object
+window.jQuery = window.$ = jQuery;
+
+// Expose jQuery as an AMD module, but only for AMD loaders that
+// understand the issues with loading multiple versions of jQuery
+// in a page that all might call define(). The loader will indicate
+// they have special allowances for multiple jQuery versions by
+// specifying define.amd.jQuery = true. Register as a named module,
+// since jQuery can be concatenated with other files that may use define,
+// but not use a proper concatenation script that understands anonymous
+// AMD modules. A named AMD is safest and most robust way to register.
+// Lowercase jquery is used because AMD module names are derived from
+// file names, and jQuery is normally delivered in a lowercase file name.
+// Do this after creating the global so that if an AMD module wants to call
+// noConflict to hide this version of jQuery, it will work.
+if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
+ define( "jquery", [], function () { return jQuery; } );
+}
+
+})( window );
diff --git a/pype/premiere/extensions/com.pype.avalon/js/json2.js b/pype/premiere/extensions/com.pype/lib/json2.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/js/json2.js
rename to pype/premiere/extensions/com.pype/lib/json2.js
diff --git a/pype/premiere/extensions/com.pype/package.json b/pype/premiere/extensions/com.pype/package.json
new file mode 100644
index 00000000000..fa61712c595
--- /dev/null
+++ b/pype/premiere/extensions/com.pype/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "com.pype",
+ "version": "1.0.0",
+ "description": "pype avalon integration",
+ "license": "ISC",
+ "main": "CSXS\\manifest.xml",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "dependencies": {
+ "bluebird": "^3.7.2",
+ "decompress-zip": "^0.2.2",
+ "fs": "^0.0.1-security",
+ "jsonfile": "^6.0.1",
+ "junk": "^3.1.0",
+ "mkdirp": "^1.0.4",
+ "node-fetch": "^2.6.0",
+ "node-timecodes": "^2.5.0",
+ "opn": "^6.0.0",
+ "os": "^0.1.1",
+ "path": "^0.12.7",
+ "process": "^0.11.10",
+ "pure-uuid": "^1.6.0",
+ "rimraf": "^3.0.2",
+ "url": "^0.11.0",
+ "walk": "^2.3.14",
+ "xml2js": "^0.4.23"
+ },
+ "devDependencies": {}
+}
diff --git a/pype/premiere/extensions/com.pype/pypeApp.jsx b/pype/premiere/extensions/com.pype/pypeApp.jsx
new file mode 100644
index 00000000000..07a1aa9492c
--- /dev/null
+++ b/pype/premiere/extensions/com.pype/pypeApp.jsx
@@ -0,0 +1,15 @@
+/* global $, File, Folder, alert */
+
+if (typeof ($) === 'undefined') {
+ var $ = {};
+}
+
+if (typeof (app) === 'undefined') {
+ var app = {};
+}
+
+function keepExtention () {
+ return app.setExtensionPersistent('com.pype', 0);
+}
+
+keepExtention();
diff --git a/pype/premiere/lib.py b/pype/premiere/lib.py
new file mode 100644
index 00000000000..f03c98fb78b
--- /dev/null
+++ b/pype/premiere/lib.py
@@ -0,0 +1,195 @@
+import os
+import sys
+import shutil
+import json
+from pysync import walktree
+import requests
+
+from avalon import api
+from pype.widgets.message_window import message
+from pypeapp import Logger
+
+
+log = Logger().get_logger(__name__, "premiere")
+
+self = sys.modules[__name__]
+self._has_been_setup = False
+self._registered_gui = None
+
+AVALON_CONFIG = os.environ["AVALON_CONFIG"]
+
+PARENT_DIR = os.path.dirname(__file__)
+PACKAGE_DIR = os.path.dirname(PARENT_DIR)
+PLUGINS_DIR = os.path.join(PACKAGE_DIR, "plugins")
+
+self.EXTENSIONS_PATH_REMOTE = os.path.join(PARENT_DIR, "extensions")
+self.EXTENSIONS_PATH_LOCAL = None
+self.EXTENSIONS_CACHE_PATH = None
+
+self.LOAD_PATH = os.path.join(PLUGINS_DIR, "premiere", "load")
+self.CREATE_PATH = os.path.join(PLUGINS_DIR, "premiere", "create")
+self.INVENTORY_PATH = os.path.join(PLUGINS_DIR, "premiere", "inventory")
+
+self.PUBLISH_PATH = os.path.join(
+ PLUGINS_DIR, "premiere", "publish"
+).replace("\\", "/")
+
+if os.getenv("PUBLISH_PATH", None):
+ if self.PUBLISH_PATH not in os.environ["PUBLISH_PATH"]:
+ os.environ["PUBLISH_PATH"] = os.pathsep.join(
+ os.environ["PUBLISH_PATH"].split(os.pathsep) +
+ [self.PUBLISH_PATH]
+ )
+else:
+ os.environ["PUBLISH_PATH"] = self.PUBLISH_PATH
+
+_clearing_cache = ["com.pype", "com.pype.rename"]
+
+
+def ls():
+ pass
+
+
+def reload_pipeline():
+ """Attempt to reload pipeline at run-time.
+
+ CAUTION: This is primarily for development and debugging purposes.
+
+ """
+
+ import importlib
+ import pype.premiere
+
+ api.uninstall()
+
+ for module in ("avalon.io",
+ "avalon.lib",
+ "avalon.pipeline",
+ "avalon.api",
+ "avalon.tools",
+
+ "{}".format(AVALON_CONFIG),
+ "{}.premiere".format(AVALON_CONFIG),
+ "{}.premiere.lib".format(AVALON_CONFIG)
+ ):
+ log.info("Reloading module: {}...".format(module))
+ try:
+ module = importlib.import_module(module)
+ importlib.reload(module)
+ except Exception as e:
+ log.warning("Cannot reload module: {}".format(e))
+
+ api.install(pype.premiere)
+
+
+def setup(env=None):
+ """ Running wrapper
+ """
+ if not env:
+ env = os.environ
+
+ self.EXTENSIONS_PATH_LOCAL = env["EXTENSIONS_PATH"]
+ self.EXTENSIONS_CACHE_PATH = env["EXTENSIONS_CACHE_PATH"]
+
+ log.info("Registering Adobe Premiere plug-ins..")
+ if not test_rest_api_server(env):
+ return
+
+ if not env.get("installed_zxp"):
+ # remove cep_cache from user temp dir
+ clearing_caches_ui()
+
+ # synchronize extensions
+ extensions_sync()
+ else:
+ log.info("Extensions installed as `.zxp`...")
+
+ log.info("Premiere Pype wrapper has been installed")
+
+
+def extensions_sync():
+ # TODO(antirotor): Bundle extension and install it
+ # we need to bundle extension as we are using third party node_modules
+ # to ease creation of bundle, lets create build script creating self-signed
+ # certificate and bundling extension to zxp format (using ZXPSignCmd from
+ # Adobe). If we find zxp in extension directory, we can install it via
+ # command line `ExManCmd /install` - using Adobe Extension Manager. If
+ # zxp is not found, we use old behaviour and just copy all files. Thus we
+ # maintain ability to develop and deploy at the same time.
+ #
+ # sources:
+ # https://helpx.adobe.com/extension-manager/using/command-line.html
+
+ process_pairs = list()
+ # get extensions dir in pype.premiere.extensions
+ # build dir path to premiere cep extensions
+
+ for name in os.listdir(self.EXTENSIONS_PATH_REMOTE):
+ log.debug("> name: {}".format(name))
+ src = os.path.join(self.EXTENSIONS_PATH_REMOTE, name)
+ dst = os.path.join(self.EXTENSIONS_PATH_LOCAL, name)
+ process_pairs.append((name, src, dst))
+
+ # synchronize all extensions
+ for name, src, dst in process_pairs:
+ if not os.path.isdir(src):
+ continue
+ if name not in _clearing_cache:
+ continue
+ if not os.path.exists(dst):
+ os.makedirs(dst, mode=0o777)
+ walktree(source=src, target=dst, options_input=["y", ">"])
+ log.info("Extension {0} from `{1}` copied to `{2}`".format(
+ name, src, dst
+ ))
+ # time.sleep(10)
+ return
+
+
+def clearing_caches_ui():
+ '''Before every start of premiere it will make sure there is not
+ outdated stuff in cep_cache dir'''
+
+ if not os.path.isdir(self.EXTENSIONS_CACHE_PATH):
+ os.makedirs(self.EXTENSIONS_CACHE_PATH, mode=0o777)
+ log.info("Created dir: {}".format(self.EXTENSIONS_CACHE_PATH))
+
+ for d in os.listdir(self.EXTENSIONS_CACHE_PATH):
+ match = [p for p in _clearing_cache
+ if str(p) in d]
+
+ if match:
+ try:
+ path = os.path.normpath(
+ os.path.join(self.EXTENSIONS_CACHE_PATH, d))
+ log.info("Removing dir: {}".format(path))
+ shutil.rmtree(path, ignore_errors=True)
+ except Exception as e:
+ log.error("problem: {}".format(e))
+
+
+def test_rest_api_server(env):
+ # from pprint import pformat
+ rest_url = env.get("PYPE_REST_API_URL")
+ project_name = "{AVALON_PROJECT}".format(**env)
+ URL = "/".join((rest_url,
+ "avalon/projects",
+ project_name))
+ log.debug("__ URL: {}".format(URL))
+ try:
+ req = requests.get(URL, data={}).text
+ req_json = json.loads(req)
+ # log.debug("_ req_json: {}".format(pformat(req_json)))
+ log.debug("__ projectName: {}".format(req_json["data"]["name"]))
+ assert req_json["data"]["name"] == project_name, (
+ "Project data from Rest API server not correct")
+ return True
+
+ except Exception as e:
+ message(title="Pype Rest API static server is not running ",
+ message=("Before you can run Premiere, make sure "
+ "the system Tray Pype icon is running and "
+ "submenu `service` with name `Rest API` is "
+ "with green icon."
+ "\n Error: {}".format(e)),
+ level="critical")
diff --git a/pype/premiere/ppro/css/avalon.min.css b/pype/premiere/ppro/css/avalon.min.css
new file mode 100644
index 00000000000..03471edfd37
--- /dev/null
+++ b/pype/premiere/ppro/css/avalon.min.css
@@ -0,0 +1,28 @@
+body {
+ background-color: #323238;
+ color: #eeeeee
+}
+
+#output {
+ background: #121212;
+ color: #eeeeee;
+ padding: 2em;
+ font-family: monospace;
+ font-weight: bold;
+ min-height: 8em
+}
+
+#output.error {
+ background: #FF0000;
+ color: #000000;
+ padding: 2em;
+ font-family: monospace;
+ font-weight: bold;
+ min-height: 8em
+}
+
+.dark>.list-group-item {
+ background: #454747
+}
+
+/*# sourceMappingURL=avalon.min.css.map */
\ No newline at end of file
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/css/avalon.min.css.map b/pype/premiere/ppro/css/avalon.min.css.map
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/css/avalon.min.css.map
rename to pype/premiere/ppro/css/avalon.min.css.map
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/css/avalon.scss b/pype/premiere/ppro/css/avalon.scss
similarity index 63%
rename from pype/premiere/extensions/com.pype.avalon/ppro/css/avalon.scss
rename to pype/premiere/ppro/css/avalon.scss
index cf06ece9be8..24cee204dda 100644
--- a/pype/premiere/extensions/com.pype.avalon/ppro/css/avalon.scss
+++ b/pype/premiere/ppro/css/avalon.scss
@@ -12,6 +12,15 @@ body {
min-height: 8em;
}
+#output.error {
+ background: #FF0000;
+ color: #000000;
+ padding: 2em;
+ font-family: monospace;
+ font-weight: bold;
+ min-height: 8em
+}
+
.dark > .list-group-item {
background: #454747;
}
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/css/bootstrap.min.css b/pype/premiere/ppro/css/bootstrap.min.css
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/css/bootstrap.min.css
rename to pype/premiere/ppro/css/bootstrap.min.css
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/css/bootstrap.min.css.map b/pype/premiere/ppro/css/bootstrap.min.css.map
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/css/bootstrap.min.css.map
rename to pype/premiere/ppro/css/bootstrap.min.css.map
diff --git a/pype/premiere/ppro/debug.log b/pype/premiere/ppro/debug.log
new file mode 100644
index 00000000000..e9526143834
--- /dev/null
+++ b/pype/premiere/ppro/debug.log
@@ -0,0 +1,48 @@
+[0403/172908.369:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.370:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.371:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.371:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/172908.371:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
+[0403/173803.977:ERROR:crash_report_database_win.cc(469)] failed to stat report
diff --git a/pype/premiere/ppro/img/blender.png b/pype/premiere/ppro/img/blender.png
new file mode 100644
index 00000000000..6070a51fae3
Binary files /dev/null and b/pype/premiere/ppro/img/blender.png differ
diff --git a/pype/premiere/ppro/index.html b/pype/premiere/ppro/index.html
new file mode 100644
index 00000000000..ba2e2cdad7d
--- /dev/null
+++ b/pype/premiere/ppro/index.html
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
Pype extention
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Refresh panel
+
+ -
+
+
+ -
+
Publish
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
Output
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pype/premiere/ppro/js/.eslintrc.json b/pype/premiere/ppro/js/.eslintrc.json
new file mode 100644
index 00000000000..4e1b23c2e40
--- /dev/null
+++ b/pype/premiere/ppro/js/.eslintrc.json
@@ -0,0 +1,69 @@
+{
+ "parserOptions": {
+ "ecmaVersion": 9,
+ "sourceType": "script",
+ "ecmaFeatures": {
+ "jsx": true
+ }
+ },
+ "rules": {
+ "constructor-super": 2,
+ "for-direction": 2,
+ "getter-return": 2,
+ "no-async-promise-executor": 2,
+ "no-case-declarations": 2,
+ "no-class-assign": 2,
+ "no-compare-neg-zero": 2,
+ "no-cond-assign": 2,
+ "no-const-assign": 2,
+ "no-constant-condition": 2,
+ "no-control-regex": 2,
+ "no-debugger": 2,
+ "no-delete-var": 2,
+ "no-dupe-args": 2,
+ "no-dupe-class-members": 2,
+ "no-dupe-keys": 2,
+ "no-duplicate-case": 2,
+ "no-empty": 2,
+ "no-empty-character-class": 2,
+ "no-empty-pattern": 2,
+ "no-ex-assign": 2,
+ "no-extra-boolean-cast": 2,
+ "no-extra-semi": 2,
+ "no-fallthrough": 2,
+ "no-func-assign": 2,
+ "no-global-assign": 2,
+ "no-inner-declarations": 2,
+ "no-invalid-regexp": 2,
+ "no-irregular-whitespace": 2,
+ "no-misleading-character-class": 2,
+ "no-mixed-spaces-and-tabs": 2,
+ "no-new-symbol": 2,
+ "no-obj-calls": 2,
+ "no-octal": 2,
+ "no-prototype-builtins": 2,
+ "no-redeclare": 2,
+ "no-regex-spaces": 2,
+ "no-self-assign": 2,
+ "no-shadow-restricted-names": 2,
+ "no-sparse-arrays": 2,
+ "no-this-before-super": 2,
+ "no-undef": 2,
+ "no-unexpected-multiline": 2,
+ "no-unreachable": 2,
+ "no-unsafe-finally": 2,
+ "no-unsafe-negation": 2,
+ "no-unused-labels": 2,
+ "no-unused-vars": 2,
+ "no-useless-catch": 2,
+ "no-useless-escape": 2,
+ "no-with": 2,
+ "require-yield": 2,
+ "use-isnan": 2,
+ "valid-typeof": 2
+ },
+ "env": {
+ "es2017": false,
+ "node": true
+ }
+}
\ No newline at end of file
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/build.js b/pype/premiere/ppro/js/build.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/build.js
rename to pype/premiere/ppro/js/build.js
diff --git a/pype/premiere/ppro/js/pype.js b/pype/premiere/ppro/js/pype.js
new file mode 100644
index 00000000000..db1cadeb3be
--- /dev/null
+++ b/pype/premiere/ppro/js/pype.js
@@ -0,0 +1,344 @@
+/* global CSInterface, $, PypeRestApiClient, SystemPath */
+/* eslint-env node, es2017, esversion:6 */
+
+class Pype {
+
+ /**
+ * Initialize important properties and load necessary JSX files.
+ */
+ constructor() {
+ this.csi = new CSInterface();
+ this.outputId = $("#output");
+
+ this.rootFolderPath = this.csi.getSystemPath(SystemPath.EXTENSION);
+ var extensionRoot = this.rootFolderPath + "/jsx/";
+ this.progress("Loading premiere.jsx", true);
+ this.csi.evalScript('$.evalFile("' + extensionRoot + '/PPRO/Premiere.jsx");', () => {
+ this.progress("Loading pype.jsx", true);
+ this.csi.evalScript('$.evalFile("' + extensionRoot + 'pype.jsx");', () => {
+ this.progress("Loading batchRenamer.jsx", true);
+ this.csi.evalScript('$.evalFile("' + extensionRoot + 'batchRenamer.jsx");', () => {
+ this._initialize();
+ });
+ });
+ });
+ }
+
+ _initialize() {
+ var self = this;
+ // get environment
+ this.csi.evalScript('$.pype.getProjectFileData();', (result) => {
+ if (result == "EvalScript error.") {
+ this.error("Cannot get project data.");
+ throw "Cannot get project data";
+ }
+ process.env.EXTENSION_PATH = this.rootFolderPath;
+ this.env = process.env;
+ var resultData = JSON.parse(result);
+ for (var key in resultData) {
+ this.env[key] = resultData[key];
+ }
+ this.csi.evalScript('$.pype.setEnvs(' + JSON.stringify(self.env) + ')');
+ this.pras = new PypeRestApiClient(this.env);
+ this.progress(`Getting presets for ${this.env.AVALON_PROJECT}`, true);
+ this.presets = this.pras.get_presets(this.env.AVALON_PROJECT)
+ .then((presets) => {
+ this.progress("transferring presets to jsx")
+ this.presets = presets;
+ this.csi.evalScript('$.pype.setProjectPreset(' + JSON.stringify(presets) + ');', () => {
+ this.progress("Panel's backend loaded...", true);
+ // bind encoding jobs event listener
+ this.csi.addEventListener("pype.EncoderJobsComplete", this._encodingDone);
+
+ // Bind Interface buttons
+ this._bindButtons();
+ });
+ });
+ });
+ }
+
+ /**
+ * Wrapper function over clip renamer
+ */
+ rename () {
+ let $renameId = $('#rename');
+ let data = {};
+ data.ep = $('input[name=episode]', $renameId).val();
+ data.epSuffix = $('input[name=ep_suffix]', $renameId).val();
+
+ if (!data.ep) {
+ this.csi.evalScript('$.pype.alert_message("' + 'Need to fill episode code' + '")');
+ return;
+ }
+
+ if (!data.epSuffix) {
+ this.csi.evalScript('$.pype.alert_message("' + 'Need to fill episode longer suffix' + '")');
+ return;
+ }
+
+ this.progress(`Doing rename [ ${data.ep} ] | [ ${data.epSuffix} ]`);
+ this.csi.evalScript(
+ 'BatchRenamer.renameTargetedTextLayer(' + JSON.stringify(data) + ' );', (result) => {
+ this.progress(`Renaming result: ${result}`, true);
+ });
+ }
+
+ _bindButtons() {
+ var self = this;
+ $('#btn-publish').click(function () {
+ self.publish();
+ });
+
+ $('#btn-rename').click(function () {
+ self.rename();
+ });
+
+ $('#btn-send-reset').click(function () {
+ $('#publish input[name=send-path]').val("");
+ });
+
+ $('#btn-get-reset').click(function () {
+ $('#publish input[name=get-path]').val("");
+ });
+
+ $('#btn-newWorkfileVersion').click(function () {
+ self.csi.evalScript('$.pype.versionUpWorkFile();');
+ self.progress('New version of the project file saved...', true);
+ });
+
+ $('#btn-get-frame').click(function () {
+ self.csi.evalScript('$._PPP_.exportCurrentFrameAsPNG();', (result) => {
+ self.progress(`Screen grabing image path in: [${result}]`, true);
+ });
+});
+ }
+
+ /**
+ * Normalize slashes in path string
+ * @param {String} path
+ */
+ static convertPathString (path) {
+ return path.replace(
+ new RegExp('\\\\', 'g'), '/').replace(new RegExp('//\\?/', 'g'), '');
+ }
+ /**
+ * Gather all user UI options for publishing
+ */
+ _gatherPublishUI() {
+ let publishId = $('#publish');
+ let uiVersionUp = $('input[name=version-up]', publishId);
+ let uiAudioOnly = $('input[name=audio-only]', publishId);
+ let uiJsonSendPath = $('input[name=send-path]', publishId);
+ let uiJsonGetPath = $('input[name=get-path]', publishId);
+ this.publishUI = {
+ "versionUp": uiVersionUp.prop('checked'),
+ "audioOnly": uiAudioOnly.prop('checked'),
+ "jsonSendPath": uiJsonSendPath.val(),
+ "jsonGetPath": uiJsonGetPath.val()
+ }
+ }
+
+ _getStagingDir() {
+ const path = require('path');
+ const UUID = require('pure-uuid');
+ const os = require('os');
+
+ const id = new UUID(4).format();
+ return path.join(os.tmpdir(), id);
+ }
+
+ /**
+ * Create staging directories and copy project files
+ * @param {object} projectData Project JSON data
+ */
+ _copyProjectFiles(projectData) {
+ const path = require('path');
+ const fs = require('fs');
+ const mkdirp = require('mkdirp');
+
+ this.stagingDir = this._getStagingDir();
+
+ this.progress(`Creating directory [ ${this.stagingDir} ]`, true);
+
+ mkdirp.sync(this.stagingDir);
+
+ let stagingDir = Pype.convertPathString(this.stagingDir);
+ const destination = Pype.convertPathString(
+ path.join(stagingDir, projectData.projectfile));
+
+ this.progress(`Copying files from [ ${projectData.projectpath} ] -> [ ${destination} ]`);
+ fs.copyFileSync(projectData.projectpath, destination);
+
+ this.progress("Project files copied.", true);
+ }
+
+ _encodeRepresentation(repre) {
+ var self = this;
+ return new Promise(function(resolve, reject) {
+ self.csi.evalScript('$.pype.encodeRepresentation(' + JSON.stringify(repre) + ');', (result) => {
+ if (result == "EvalScript error.") {
+ reject(result);
+ }
+ self.progress("Encoding files to Encoder queue submitted ...", true);
+ const jsonfile = require('jsonfile');
+ let jsonContent = JSON.parse(result);
+ if (self.publishUI.jsonSendPath == "") {
+ self.publishUI.jsonSendPath = self.stagingDir + "\\publishSend.json";
+ $('#publish input[name=send-path]').val(self.publishUI.jsonSendPath);
+ }
+ if (self.publishUI.jsonGetPath == "") {
+ self.publishUI.jsonGetPath = self.stagingDir + "_publishGet.json";
+ $('#publish input[name=get-path]').val(self.publishUI.jsonGetPath);
+ }
+ jsonfile.writeFile(self.publishUI.jsonSendPath, jsonContent);
+ resolve(result);
+ });
+ });
+ }
+
+ _getPyblishRequest(stagingDir) {
+ var self = this;
+ return new Promise(function(resolve, reject) {
+ self.csi.evalScript("$.pype.getPyblishRequest('" + stagingDir + "', '" + self.publishUI.audioOnly + "');", (result) => {
+ if (result === "null" || result === "EvalScript error.") {
+ self.error(`cannot create publish request data ${result}`);
+ reject("cannot create publish request data");
+ } else {
+ console.log(`Request generated: ${result}`);
+ resolve(result);
+ }
+ });
+ });
+ }
+
+ publish() {
+ this._gatherPublishUI();
+ if (this.publishUI.jsonSendPath === "") {
+ // path is empty, so we first prepare data for publishing
+ // and create json
+
+ this.progress("Gathering project data ...", true);
+ this.csi.evalScript('$.pype.getProjectFileData();', (result) => {
+ this._copyProjectFiles(JSON.parse(result))
+ // create request and start encoding
+ // after that is done, we should recieve event and continue in
+ // _encodingDone()
+ this.progress("Creating publishing request ...", true)
+ this._getPyblishRequest(Pype.convertPathString(this.stagingDir))
+ .then(result => {
+ this.progress('Encoding ...');
+ this._encodeRepresentation(JSON.parse(result))
+ .then(result => {
+ console.log('printing result from enconding.. ' + result);
+ })
+ .catch(error => {
+ this.error(`failed to encode: ${error}`);
+ });
+ }, error => {
+ this.error(`failed to publish: ${error}`);
+ });
+ this.progress("Waiting for result ...");
+ });
+ } else {
+ // load request
+ var dataToPublish = {
+ "adobePublishJsonPathSend": this.publishUI.jsonSendPath,
+ "adobePublishJsonPathGet": this.publishUI.jsonGetPath,
+ "project": this.env.AVALON_PROJECT,
+ "asset": this.env.AVALON_ASSET,
+ "task": this.env.AVALON_TASK,
+ "workdir": Pype.convertPathString(this.env.AVALON_WORKDIR),
+ "AVALON_APP": this.env.AVALON_APP,
+ "AVALON_APP_NAME": this.env.AVALON_APP_NAME
+ }
+ // C:\Users\jezsc\AppData\Local\Temp\4c56ba52-8839-44de-b327-0187c79d0814\publishSend.json
+ this.pras.publish(JSON.stringify(dataToPublish))
+ .then((result) => {
+ const fs = require('fs');
+ if (fs.existsSync(result.return_data_path)) {
+ if (this.publishUI.versionUp) {
+ this.progress('Saving new version of the project file', true);
+ this.csi.evalScript('$.pype.versionUpWorkFile();');
+ }
+ // here jsonSetPath and jsonGetPath are set to gui
+ $('#publish input[name=send-path]').val("");
+ $('#publish input[name=get-path]').val("");
+ this.progress("Publishing done.", true);
+ } else {
+ this.error("Publish has not finished correctly");
+ throw "Publish has not finished correctly";
+ }
+ }, (error) => {
+ this.error("Invalid response from server");
+ console.error(error);
+ });
+ }
+ }
+
+ _encodingDone(event) {
+ // this will be global in this context
+ console.debug(event);
+ this.pype.progress("Publishing event after encoding finished recieved ...", true);
+ var dataToPublish = {
+ "adobePublishJsonPathSend": this.pype.publishUI.jsonSendPath,
+ "adobePublishJsonPathGet": this.pype.publishUI.jsonGetPath,
+ "gui": true,
+ // "publishPath": Pype.convertPathString(this.pype.env.PUBLISH_PATH),
+ "project": this.pype.env.AVALON_PROJECT,
+ "asset": this.pype.env.AVALON_ASSET,
+ "task": this.pype.env.AVALON_TASK,
+ "workdir": Pype.convertPathString(this.pype.env.AVALON_WORKDIR),
+ "AVALON_APP": this.pype.env.AVALON_APP,
+ "AVALON_APP_NAME": this.pype.env.AVALON_APP_NAME
+ }
+
+ this.pype.progress("Preparing publish ...", true);
+ console.log(JSON.stringify(dataToPublish));
+ this.pype.pras.publish(JSON.stringify(dataToPublish))
+ .then((result) => {
+ const fs = require('fs');
+ if (fs.existsSync(result.return_data_path)) {
+ if (this.pype.publishUI.versionUp) {
+ this.pype.progress('Saving new version of the project file', true);
+ this.pype.csi.evalScript('$.pype.versionUpWorkFile();');
+ }
+ // here jsonSetPath and jsonGetPath are set to gui
+ $('#publish input[name=send-path]').val("");
+ $('#publish input[name=get-path]').val("");
+ this.pype.progress("Publishing done.", true);
+ } else {
+ this.pype.error("Publish has not finished correctly")
+ throw "Publish has not finished correctly";
+ }
+ }, (error) => {
+ this.pype.error("Invalid response from server");
+ console.error(error);
+ });
+ }
+
+ /**
+ * Display error message in div
+ * @param {String} message
+ */
+ error(message) {
+ this.outputId.html(message);
+ this.outputId.addClass("error");
+ console.error(message);
+ }
+
+ /**
+ * Display message in output div. If append is set, new message is appended to rest with
+ * @param {String} message
+ * @param {Boolean} append
+ */
+ progress(message, append=false) {
+ this.outputId.removeClass("error");
+ if (append) {
+ this.outputId.prepend(message + "
");
+ }
+ console.info(message);
+ }
+}
+$(function() {
+ global.pype = new Pype();
+});
diff --git a/pype/premiere/ppro/js/pype_restapi_client.js b/pype/premiere/ppro/js/pype_restapi_client.js
new file mode 100644
index 00000000000..b9a5ec9425b
--- /dev/null
+++ b/pype/premiere/ppro/js/pype_restapi_client.js
@@ -0,0 +1,69 @@
+/* eslint-env node, es2017, esversion:6 */
+
+// connecting pype module pype rest api server (pras)
+
+
+class PypeRestApiClient {
+
+ constructor(env) {
+ this.env = env;
+ }
+
+ /**
+ * Return url for pype rest api server service
+ * @return {url string}
+ */
+ _getApiServerUrl() {
+ var url = this.env.PYPE_REST_API_URL;
+ return url
+ }
+
+ /**
+ * Return JSON from server. This will wait for result.
+ * @todo handle status codes and non-json data
+ * @param {String} url server url
+ * @param {object} options request options
+ */
+ async getResponseFromRestApiServer(url, options = {}) {
+ const fetch = require('node-fetch');
+ let defaults = {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json"
+ }
+ }
+ let settings = {...defaults, ...options}
+ const res = await fetch(url, settings);
+ return await res.json();
+ }
+
+
+ /**
+ * Return presets for project from server
+ * @param {String} projectName
+ */
+ async get_presets(projectName) {
+ let server = this._getApiServerUrl();
+ let url = `${server}/adobe/presets/${projectName}`;
+ console.log("connecting ...");
+ let response = await this.getResponseFromRestApiServer(url)
+ console.log("got presets:");
+ console.log(response.data);
+ return response.data;
+ }
+
+ async publish(data) {
+ let server = this._getApiServerUrl();
+ let url = `${server}/adobe/publish`;
+
+ let headers = {
+ "Content-Type": "application/json"
+ }
+ console.log("connecting ...");
+ let response = await this.getResponseFromRestApiServer(
+ url, {method: 'POST', headers: headers, body: data});
+ console.log("got response:");
+ console.log(response.data);
+ return response.data;
+ }
+}
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/CSInterface-8.js b/pype/premiere/ppro/js/vendor/CSInterface-8.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/CSInterface-8.js
rename to pype/premiere/ppro/js/vendor/CSInterface-8.js
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/bootstrap.min.js b/pype/premiere/ppro/js/vendor/bootstrap.min.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/bootstrap.min.js
rename to pype/premiere/ppro/js/vendor/bootstrap.min.js
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/bootstrap.min.js.map b/pype/premiere/ppro/js/vendor/bootstrap.min.js.map
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/bootstrap.min.js.map
rename to pype/premiere/ppro/js/vendor/bootstrap.min.js.map
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/jquery-3.3.1.min.js b/pype/premiere/ppro/js/vendor/jquery-3.3.1.min.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/jquery-3.3.1.min.js
rename to pype/premiere/ppro/js/vendor/jquery-3.3.1.min.js
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/json2.js b/pype/premiere/ppro/js/vendor/json2.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/json2.js
rename to pype/premiere/ppro/js/vendor/json2.js
diff --git a/pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/popper.min.js b/pype/premiere/ppro/js/vendor/popper.min.js
similarity index 100%
rename from pype/premiere/extensions/com.pype.avalon/ppro/js/vendor/popper.min.js
rename to pype/premiere/ppro/js/vendor/popper.min.js
diff --git a/pype/premiere/templates.py b/pype/premiere/templates.py
deleted file mode 100644
index 33a7a6ff61b..00000000000
--- a/pype/premiere/templates.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from pype import api as pype
-
-log = pype.Logger.getLogger(__name__, "premiere")
-
-
-def get_anatomy(**kwarg):
- return pype.Anatomy
-
-
-def get_dataflow(**kwarg):
- log.info(kwarg)
- host = kwarg.get("host", "premiere")
- cls = kwarg.get("class", None)
- preset = kwarg.get("preset", None)
- assert any([host, cls]), log.error("premiera.templates.get_dataflow():"
- "Missing mandatory kwargs `host`, `cls`")
-
- pr_dataflow = getattr(pype.Dataflow, str(host), None)
- pr_dataflow_node = getattr(pr_dataflow.nodes, str(cls), None)
- if preset:
- pr_dataflow_node = getattr(pr_dataflow_node, str(preset), None)
-
- log.info("Dataflow: {}".format(pr_dataflow_node))
- return pr_dataflow_node
-
-
-def get_colorspace(**kwarg):
- log.info(kwarg)
- host = kwarg.get("host", "premiere")
- cls = kwarg.get("class", None)
- preset = kwarg.get("preset", None)
- assert any([host, cls]), log.error("premiera.templates.get_colorspace():"
- "Missing mandatory kwargs `host`, `cls`")
-
- pr_colorspace = getattr(pype.Colorspace, str(host), None)
- pr_colorspace_node = getattr(pr_colorspace, str(cls), None)
- if preset:
- pr_colorspace_node = getattr(pr_colorspace_node, str(preset), None)
-
- log.info("Colorspace: {}".format(pr_colorspace_node))
- return pr_colorspace_node
diff --git a/pype/services/adobe_communicator/__init__.py b/pype/services/adobe_communicator/__init__.py
new file mode 100644
index 00000000000..4110ab69b54
--- /dev/null
+++ b/pype/services/adobe_communicator/__init__.py
@@ -0,0 +1,5 @@
+from .adobe_comunicator import AdobeCommunicator
+
+
+def tray_init(tray_widget, main_widget):
+ return AdobeCommunicator()
diff --git a/pype/services/adobe_communicator/adobe_comunicator.py b/pype/services/adobe_communicator/adobe_comunicator.py
new file mode 100644
index 00000000000..d842955781f
--- /dev/null
+++ b/pype/services/adobe_communicator/adobe_comunicator.py
@@ -0,0 +1,49 @@
+import os
+import pype
+from pypeapp import Logger
+from .lib import AdobeRestApi, PUBLISH_PATHS
+
+log = Logger().get_logger("AdobeCommunicator")
+
+
+class AdobeCommunicator:
+ rest_api_obj = None
+
+ def __init__(self):
+ self.rest_api_obj = None
+
+ # Add "adobecommunicator" publish paths
+ PUBLISH_PATHS.append(os.path.sep.join(
+ [pype.PLUGINS_DIR, "adobecommunicator", "publish"]
+ ))
+
+ def tray_start(self):
+ return
+
+ def process_modules(self, modules):
+ # Module requires RestApiServer
+ rest_api_module = modules.get("RestApiServer")
+ if not rest_api_module:
+ log.warning(
+ "AdobeCommunicator won't work without RestApiServer."
+ )
+ return
+
+ # Register statics url
+ pype_module_root = os.environ["PYPE_MODULE_ROOT"].replace("\\", "/")
+ static_path = "{}/pype/premiere/ppro".format(pype_module_root)
+ rest_api_module.register_statics("/ppro", static_path)
+
+ # Register rest api object for communication
+ self.rest_api_obj = AdobeRestApi()
+
+ # Add Ftrack publish path if registered Ftrack mdule
+ if "FtrackModule" in modules:
+ PUBLISH_PATHS.append(os.path.sep.join(
+ [pype.PLUGINS_DIR, "ftrack", "publish"]
+ ))
+
+ log.debug((
+ f"Adobe Communicator Registered PUBLISH_PATHS"
+ f"> `{PUBLISH_PATHS}`"
+ ))
diff --git a/pype/services/adobe_communicator/lib/__init__.py b/pype/services/adobe_communicator/lib/__init__.py
new file mode 100644
index 00000000000..23aee81275c
--- /dev/null
+++ b/pype/services/adobe_communicator/lib/__init__.py
@@ -0,0 +1,8 @@
+from .io_nonsingleton import DbConnector
+from .rest_api import AdobeRestApi, PUBLISH_PATHS
+
+__all__ = [
+ "PUBLISH_PATHS",
+ "DbConnector",
+ "AdobeRestApi"
+]
diff --git a/pype/aport/io_nonsingleton.py b/pype/services/adobe_communicator/lib/io_nonsingleton.py
similarity index 93%
rename from pype/aport/io_nonsingleton.py
rename to pype/services/adobe_communicator/lib/io_nonsingleton.py
index ddda21a570d..6380e4eb233 100644
--- a/pype/aport/io_nonsingleton.py
+++ b/pype/services/adobe_communicator/lib/io_nonsingleton.py
@@ -15,7 +15,7 @@
import contextlib
from avalon import schema
-import requests
+from avalon.vendor import requests
# Third-party dependencies
import pymongo
@@ -43,12 +43,25 @@ class DbConnector(object):
log = logging.getLogger(__name__)
def __init__(self):
- self.Session = {}
- self._mongo_client = None
- self._sentry_client = None
- self._sentry_logging_handler = None
- self._database = None
- self._is_installed = False
+ self.Session = {}
+ self._mongo_client = None
+ self._sentry_client = None
+ self._sentry_logging_handler = None
+ self._database = None
+ self._is_installed = False
+
+ def __getitem__(self, key):
+ # gives direct access to collection withou setting `active_table`
+ return self._database[key]
+
+ def __getattribute__(self, attr):
+ # not all methods of PyMongo database are implemented with this it is
+ # possible to use them too
+ try:
+ return super(DbConnector, self).__getattribute__(attr)
+ except AttributeError:
+ cur_proj = self.Session["AVALON_PROJECT"]
+ return self._database[cur_proj].__getattribute__(attr)
def install(self):
"""Establish a persistent connection to the database"""
@@ -192,7 +205,7 @@ def _from_environment(self):
) if os.getenv(item[0], item[1]) is not None
}
- Session["schema"] = "avalon-core:session-1.0"
+ Session["schema"] = "avalon-core:session-2.0"
try:
schema.validate(Session)
except schema.ValidationError as e:
@@ -320,6 +333,7 @@ def find(self, filter, projection=None, sort=None):
@auto_reconnect
def find_one(self, filter, projection=None, sort=None):
assert isinstance(filter, dict), "filter must be
"
+
return self._database[self.Session["AVALON_PROJECT"]].find_one(
filter=filter,
projection=projection,
diff --git a/pype/services/adobe_communicator/lib/publish.py b/pype/services/adobe_communicator/lib/publish.py
new file mode 100644
index 00000000000..2e7d993a609
--- /dev/null
+++ b/pype/services/adobe_communicator/lib/publish.py
@@ -0,0 +1,57 @@
+import os
+import sys
+import pype
+import importlib
+import pyblish.api
+import pyblish.util
+import avalon.api
+from avalon.tools import publish
+from pypeapp import Logger
+
+log = Logger().get_logger(__name__)
+
+
+def main(env):
+ # Registers pype's Global pyblish plugins
+ pype.install()
+
+ # Register Host (and it's pyblish plugins)
+ host_name = env["AVALON_APP"]
+ # TODO not sure if use "pype." or "avalon." for host import
+ host_import_str = f"avalon.{host_name}"
+
+ try:
+ host_module = importlib.import_module(host_import_str)
+ except ModuleNotFoundError:
+ log.error((
+ f"Host \"{host_name}\" can't be imported."
+ f" Import string \"{host_import_str}\" failed."
+ ))
+ return False
+
+ avalon.api.install(host_module)
+
+ # Register additional paths
+ addition_paths_str = env.get("PUBLISH_PATHS") or ""
+ addition_paths = addition_paths_str.split(os.pathsep)
+ for path in addition_paths:
+ path = os.path.normpath(path)
+ if not os.path.exists(path):
+ continue
+
+ pyblish.api.register_plugin_path(path)
+
+ # Register project specific plugins
+ project_name = os.environ["AVALON_PROJECT"]
+ project_plugins_paths = env.get("PYPE_PROJECT_PLUGINS") or ""
+ for path in project_plugins_paths.split(os.pathsep):
+ plugin_path = os.path.join(path, project_name, "plugins")
+ if os.path.exists(plugin_path):
+ pyblish.api.register_plugin_path(plugin_path)
+
+ return publish.show()
+
+
+if __name__ == "__main__":
+ result = main(os.environ)
+ sys.exit(not bool(result))
diff --git a/pype/services/adobe_communicator/lib/rest_api.py b/pype/services/adobe_communicator/lib/rest_api.py
new file mode 100644
index 00000000000..2372c4ed200
--- /dev/null
+++ b/pype/services/adobe_communicator/lib/rest_api.py
@@ -0,0 +1,117 @@
+import os
+import sys
+import copy
+from pype.services.rest_api import RestApi, route, abort, CallbackResult
+from .io_nonsingleton import DbConnector
+from pypeapp import config, execute, Logger
+
+log = Logger().get_logger("AdobeCommunicator")
+
+CURRENT_DIR = os.path.dirname(__file__)
+PUBLISH_SCRIPT_PATH = os.path.join(CURRENT_DIR, "publish.py")
+
+PUBLISH_PATHS = []
+
+
+class AdobeRestApi(RestApi):
+ dbcon = DbConnector()
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.dbcon.install()
+
+ @route("/available", "/adobe")
+ def available(self):
+ return CallbackResult()
+
+ @route("/presets/", "/adobe")
+ def get_presets(self, request):
+ project_name = request.url_data["project_name"]
+ return CallbackResult(data=config.get_presets(project_name))
+
+ @route("/publish", "/adobe", "POST")
+ def publish(self, request):
+ """Triggers publishing script in subprocess.
+
+ The subprocess freeze process and during publishing is not possible to
+ handle other requests and is possible that freeze main application.
+
+ TODO: Freezing issue may be fixed with socket communication.
+
+ Example url:
+ http://localhost:8021/adobe/publish (POST)
+ """
+ try:
+ publish_env = self._prepare_publish_environments(
+ request.request_data
+ )
+ except Exception as exc:
+ log.warning(
+ "Failed to prepare environments for publishing.",
+ exc_info=True
+ )
+ abort(400, str(exc))
+
+ output_data_path = publish_env["AC_PUBLISH_OUTPATH"]
+
+ log.info("Pyblish is running")
+ try:
+ # Trigger subprocess
+ # QUESTION should we check returncode?
+ returncode = execute(
+ [sys.executable, PUBLISH_SCRIPT_PATH],
+ env=publish_env
+ )
+
+ # Check if output file exists
+ if returncode != 0 or not os.path.exists(output_data_path):
+ abort(500, "Publishing failed")
+
+ log.info("Pyblish have stopped")
+
+ return CallbackResult(
+ data={"return_data_path": output_data_path}
+ )
+
+ except Exception:
+ log.warning("Publishing failed", exc_info=True)
+ abort(500, "Publishing failed")
+
+ def _prepare_publish_environments(self, data):
+ """Prepares environments based on request data."""
+ env = copy.deepcopy(os.environ)
+
+ project_name = data["project"]
+ asset_name = data["asset"]
+
+ project_doc = self.dbcon[project_name].find_one({
+ "type": "project"
+ })
+ av_asset = self.dbcon[project_name].find_one({
+ "type": "asset",
+ "name": asset_name
+ })
+ parents = av_asset["data"]["parents"]
+ hierarchy = ""
+ if parents:
+ hierarchy = "/".join(parents)
+
+ env["AVALON_PROJECT"] = project_name
+ env["AVALON_ASSET"] = asset_name
+ env["AVALON_TASK"] = data["task"]
+ env["AVALON_WORKDIR"] = data["workdir"]
+ env["AVALON_HIERARCHY"] = hierarchy
+ env["AVALON_PROJECTCODE"] = project_doc["data"].get("code", "")
+ env["AVALON_APP"] = data["AVALON_APP"]
+ env["AVALON_APP_NAME"] = data["AVALON_APP_NAME"]
+
+ env["PYBLISH_HOSTS"] = data["AVALON_APP"]
+
+ env["PUBLISH_PATHS"] = os.pathsep.join(PUBLISH_PATHS)
+
+ # Input and Output paths where source data and result data will be
+ # stored
+ env["AC_PUBLISH_INPATH"] = data["adobePublishJsonPathSend"]
+ env["AC_PUBLISH_OUTPATH"] = data["adobePublishJsonPathGet"]
+
+ return env
diff --git a/pype/services/rest_api/lib/factory.py b/pype/services/rest_api/lib/factory.py
index 2b94d498ff5..39ea8474fa1 100644
--- a/pype/services/rest_api/lib/factory.py
+++ b/pype/services/rest_api/lib/factory.py
@@ -3,6 +3,7 @@
import inspect
import collections
from .lib import RestMethods
+from queue import Queue
from pypeapp import Logger
@@ -208,7 +209,7 @@ class _RestApiFactory:
"""
registered_objs = []
unprocessed_routes = []
- unprocessed_statics = []
+ unprocessed_statics = Queue()
prepared_routes = {
method: collections.defaultdict(list) for method in RestMethods
@@ -225,11 +226,6 @@ def _process_route(self, route):
self.unprocessed_routes.index(route)
)
- def _process_statics(self, item):
- return self.unprocessed_statics.pop(
- self.unprocessed_statics.index(item)
- )
-
def register_route(
self, path, callback, url_prefix, methods, strict_match
):
@@ -251,7 +247,7 @@ def register_obj(self, obj):
def register_statics(self, item):
log.debug("Registering statics path \"{}\"".format(item))
- self.unprocessed_statics.append(item)
+ self.unprocessed_statics.put(item)
def _prepare_route(self, route):
"""Prepare data of registered callbacks for routes.
@@ -290,8 +286,9 @@ def prepare_registered(self):
methods has `__self__` or are defined in (it is expeted they
do not requise access to object)
"""
- for url_prefix, dir_path in self.unprocessed_statics:
- self._process_statics((url_prefix, dir_path))
+
+ while not self.unprocessed_statics.empty():
+ url_prefix, dir_path = self.unprocessed_statics.get()
dir_path = os.path.normpath(dir_path)
if not os.path.exists(dir_path):
log.warning(
@@ -314,7 +311,7 @@ def prepare_registered(self):
if not method.restapi:
continue
- for route in self.unprocessed_routes:
+ for route in list(self.unprocessed_routes):
callback = route["callback"]
if not (
callback.__qualname__ == method.__qualname__ and
@@ -330,7 +327,7 @@ def prepare_registered(self):
self._prepare_route(route)
break
- for route in self.unprocessed_routes:
+ for route in list(self.unprocessed_routes):
callback = route["callback"]
is_class_method = len(callback.__qualname__.split(".")) != 1
if is_class_method:
diff --git a/pype/services/rest_api/lib/handler.py b/pype/services/rest_api/lib/handler.py
index 732061c0055..dc948082379 100644
--- a/pype/services/rest_api/lib/handler.py
+++ b/pype/services/rest_api/lib/handler.py
@@ -252,7 +252,14 @@ def _handle_callback(self, item, parsed_url, rest_method):
content_length = int(cont_len)
in_data_str = self.rfile.read(content_length)
if in_data_str:
- in_data = json.loads(in_data_str)
+ try:
+ in_data = json.loads(in_data_str)
+ except Exception as e:
+ log.error("Invalid JSON recieved:")
+ log.error("-" * 80)
+ log.error(in_data_str)
+ log.error("-" * 80)
+ raise Exception("Invalid JSON recieved") from e
request_info = RequestInfo(
url_data=url_data,
diff --git a/pype/services/rest_api/rest_api.py b/pype/services/rest_api/rest_api.py
index 21c4f7f51f8..d600a3a8a8a 100644
--- a/pype/services/rest_api/rest_api.py
+++ b/pype/services/rest_api/rest_api.py
@@ -1,8 +1,9 @@
import os
import socket
-import socketserver
from Qt import QtCore
+from socketserver import ThreadingMixIn
+from http.server import HTTPServer
from .lib import RestApiFactory, Handler
from .base_class import route, register_statics
from pypeapp import config, Logger
@@ -10,6 +11,10 @@
log = Logger().get_logger("RestApiServer")
+class ThreadingSimpleServer(ThreadingMixIn, HTTPServer):
+ pass
+
+
class RestApiServer:
"""Rest Api allows to access statics or callbacks with http requests.
@@ -180,7 +185,8 @@ def run(self):
"Running Rest Api server on URL:"
" \"http://localhost:{}\"".format(self.port)
)
- with socketserver.TCPServer(("", self.port), Handler) as httpd:
+
+ with ThreadingSimpleServer(("", self.port), Handler) as httpd:
while self.is_running:
httpd.handle_request()
except Exception:
diff --git a/pype/vendor/pysync.py b/pype/vendor/pysync.py
new file mode 100644
index 00000000000..14a6dda34c3
--- /dev/null
+++ b/pype/vendor/pysync.py
@@ -0,0 +1,216 @@
+#!/usr/local/bin/python3
+# https://github.com/snullp/pySync/blob/master/pySync.py
+
+import sys
+import shutil
+import os
+import time
+import configparser
+from os.path import (
+ getsize,
+ getmtime,
+ isfile,
+ isdir,
+ join,
+ abspath,
+ expanduser,
+ realpath
+)
+import logging
+
+log = logging.getLogger(__name__)
+
+ignoreFiles = ("Thumbs.db", ".DS_Store")
+
+# this feature is not yet implemented
+ignorePaths = []
+
+if os.name == 'nt':
+ # msvcrt can't function correctly in IDLE
+ if 'idlelib.run' in sys.modules:
+ print("Please don't run this script in IDLE.")
+ sys.exit(0)
+ import msvcrt
+
+ def flush_input(str, set=None):
+ if not set:
+ while msvcrt.kbhit():
+ ch = msvcrt.getch()
+ if ch == '\xff':
+ print("msvcrt is broken, this is weird.")
+ sys.exit(0)
+ return input(str)
+ else:
+ return set
+else:
+ import select
+
+ def flush_input(str, set=None):
+ if not set:
+ while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
+ os.read(sys.stdin.fileno(), 4096)
+ return input(str)
+ else:
+ return set
+
+
+def compare(fa, fb, options_input=[]):
+ if isfile(fa) == isfile(fb):
+ if isdir(fa):
+ walktree(fa, fb, options_input)
+ elif isfile(fa):
+ if getsize(fa) != getsize(fb) \
+ or int(getmtime(fa)) != int(getmtime(fb)):
+ log.info(str((fa, ': size=', getsize(fa), 'mtime=',
+ time.asctime(time.localtime(getmtime(fa))))))
+ log.info(str((fb, ': size=', getsize(fb), 'mtime=',
+ time.asctime(time.localtime(getmtime(fb))))))
+ if getmtime(fa) > getmtime(fb):
+ act = '>'
+ else:
+ act = '<'
+
+ set = [i for i in options_input if i in [">", "<"]][0]
+
+ s = flush_input('What to do?(>,<,r,n)[' + act + ']', set=set)
+ if len(s) > 0:
+ act = s[0]
+ if act == '>':
+ shutil.copy2(fa, fb)
+ elif act == '<':
+ shutil.copy2(fb, fa)
+ elif act == 'r':
+ if isdir(fa):
+ shutil.rmtree(fa)
+ elif isfile(fa):
+ os.remove(fa)
+ else:
+ log.info(str(('Remove: Skipping', fa)))
+ if isdir(fb):
+ shutil.rmtree(fb)
+ elif isfile(fb):
+ os.remove(fb)
+ else:
+ log.info(str(('Remove: Skipping', fb)))
+
+ else:
+ log.debug(str(('Compare: Skipping non-dir and non-file', fa)))
+ else:
+ log.error(str(('Error:', fa, ',', fb, 'have different file type')))
+
+
+def copy(fa, fb, options_input=[]):
+ set = [i for i in options_input if i in ["y"]][0]
+ s = flush_input('Copy ' + fa + ' to another side?(r,y,n)[y]', set=set)
+ if len(s) > 0:
+ act = s[0]
+ else:
+ act = 'y'
+ if act == 'y':
+ if isdir(fa):
+ shutil.copytree(fa, fb)
+ elif isfile(fa):
+ shutil.copy2(fa, fb)
+ else:
+ log.debug(str(('Copy: Skipping ', fa)))
+ elif act == 'r':
+ if isdir(fa):
+ shutil.rmtree(fa)
+ elif isfile(fa):
+ os.remove(fa)
+ else:
+ log.debug(str(('Remove: Skipping ', fa)))
+
+
+stoentry = []
+tarentry = []
+
+
+def walktree(source, target, options_input=[]):
+ srclist = os.listdir(source)
+ tarlist = os.listdir(target)
+ if '!sync' in srclist:
+ return
+ if '!sync' in tarlist:
+ return
+ # files in source dir...
+ for f in srclist:
+ if f in ignoreFiles:
+ continue
+ spath = join(source, f)
+ tpath = join(target, f)
+ if spath in ignorePaths:
+ continue
+ if spath in stoentry:
+ # just in case target also have this one
+ if f in tarlist:
+ del tarlist[tarlist.index(f)]
+ continue
+
+ # if also exists in target dir
+ if f in tarlist:
+ del tarlist[tarlist.index(f)]
+ compare(spath, tpath, options_input)
+
+ # exists in source dir only
+ else:
+ copy(spath, tpath, options_input)
+
+ # exists in target dir only
+ set = [i for i in options_input if i in ["<"]]
+
+ for f in tarlist:
+ if f in ignoreFiles:
+ continue
+ spath = join(source, f)
+ tpath = join(target, f)
+ if tpath in ignorePaths:
+ continue
+ if tpath in tarentry:
+ continue
+ if set:
+ copy(tpath, spath, options_input)
+ else:
+ print("REMOVING: {}".format(f))
+ if os.path.isdir(tpath):
+ shutil.rmtree(tpath)
+ else:
+ os.remove(tpath)
+ print("REMOVING: {}".format(f))
+
+
+if __name__ == '__main__':
+ stoconf = configparser.RawConfigParser()
+ tarconf = configparser.RawConfigParser()
+ stoconf.read("pySync.ini")
+ tarconf.read(expanduser("~/.pysync"))
+ stoname = stoconf.sections()[0]
+ tarname = tarconf.sections()[0]
+
+ # calculate storage's base folder
+ if stoconf.has_option(stoname, 'BASE'):
+ stobase = abspath(stoconf.get(stoname, 'BASE'))
+ stoconf.remove_option(stoname, 'BASE')
+ else:
+ stobase = os.getcwd()
+
+ # same, for target's base folder
+ if tarconf.has_option(tarname, 'BASE'):
+ tarbase = abspath(tarconf.get(tarname, 'BASE'))
+ tarconf.remove_option(tarname, 'BASE')
+ else:
+ tarbase = expanduser('~/')
+
+ print("Syncing between", stoname, "and", tarname)
+ sto_content = {x: realpath(join(stobase, stoconf.get(stoname, x)))
+ for x in stoconf.options(stoname)}
+ tar_content = {x: realpath(join(tarbase, tarconf.get(tarname, x)))
+ for x in tarconf.options(tarname)}
+ stoentry = [sto_content[x] for x in sto_content]
+ tarentry = [tar_content[x] for x in tar_content]
+
+ for folder in sto_content:
+ if folder in tar_content:
+ print('Processing', folder)
+ walktree(sto_content[folder], tar_content[folder], options_input)
+ print("Done.")